2013-02-12 4 views
8

Zunächst möchte ich sagen, dass ich Software für Mikrocontroller entwickle, daher ist RAM-Nutzung wichtig und es macht Sinn, große Blöcke von const Daten in nichtflüchtigen (Flash) Speicher zu setzen.Gibt es eine nette Möglichkeit, "Split-Objekt" in C++ zu erstellen?

Was ich versuche zu erreichen, ist eine schöne Möglichkeit zu finden, ein "Split" -Objekt in C++ zu erstellen. Als Beispiel nehmen wir an, dass es ein Byte Daten (Lesen/Schreiben) und einen Multibyte- "Beleg" gibt, um darauf zuzugreifen. Nehmen wir an, dass "receipt" eine lange Zeichenfolge ist, die ein Dateiname ist und das Medium, auf das es zeigt, langsam ist. Daher ist es sinnvoll, das einzelne Byte im Speicher zu puffern, anstatt es bei jeder Anforderung zu lesen.

class Data 
{ 
    uint8_t byte; 
    bool valid; 
    const char filename[128]; 
    uint8_t read() 
    { 
     if (!valid) 
      performReallySlowRead(filename, &byte); 
     valid = true; 
     return byte; 
    }; 
    void write(uint8_t new_value) 
    { 
     byte = new_value; 
     performRealWriteToMedia(filename, byte); 
     valid = true; 
    }; 
} 

Das offensichtliche Problem bei diesem Ansatz ist, dass ganze 130-Byte RAM enden, während nur zwei von ihnen geändert werden müssen. So habe ich mit einer Idee von Split-Objekt kommen:

class DataNonConst 
{ 
    uint8_t byte; 
    bool valid; 
} 

class DataConst 
{ 
    const char filename[128]; 
    DataNonConst * const data; 
} 

static DataNonConst storage; 
const DataConst holder("some_long_name_here", &storage); 

nun das einzige Problem ist, dass, wenn ich ein paar hundert solcher haben möchte Split-Objekte den Prozess, sie schaffen (so TWO Schaffung Objekte und Verknüpfen von Sekunde zu Anfang) wird ziemlich langweilig und problematisch ...

Also die Frage ist - gibt es eine nette Möglichkeit, es einfacher zu verwenden, vorzugsweise eine clevere C++ - Trick oder vielleicht eine Vorlage Magie? Das ist - wie man ZWEI Objekte, die miteinander verbunden sind, mit einer einzigen Aussage erzeugt, vorzugsweise ist ein Objekt versteckt? Ich glaube nicht, dass eine Makro-Lösung hier möglich ist, da es keine einfache Möglichkeit gibt, die Erstellung des Namens für ein Speicherobjekt zu automatisieren. Die Objekte müssen vom gleichen Typ sein, da ich Zeiger auf solche Objekte an anderen Stellen einbetten muss (Eine Funktion beschäftigt sich damit, sie zu schreiben, andere nur das Lesen). Alle Lösungen, an die ich gedacht habe, erfordern die Verwendung einer virtuellen Schnittstelle zu Vorlagen (damit Sie das Objekt durch den vtable pointer vergrößern UND wahrscheinlich eine Bonusvorlage erhalten -bloat) oder führen in großer Vorlage aufblasen ...

EDIT:

Eigentlich Teil des ganzen Problems kann auf eine einfachere Frage reduziert werden - ist es eine Möglichkeit, zu „binden“ eine anonyme Variable ein Mitgliedsfeld in C++? Sth wie:

const ConstData holder("...", NonConstData()); // this object HAS TO be ROMable 

In dem oben "Wunschdenken" holder ist konstantes Objekt in ROM und es hat einen Zeiger/reference/was auch immer an ein anonymes Objekt NonConstData erstellt "irgendwo" im RAM. Oder:

std:pair<const ConstData &, NonConstData &> holder(ConstData(), NonConstData()); 

Alles, was mir nicht manuell erlauben würde, um beide Objekte zu erstellen und einem zum anderen binden.

+0

Was in Halter für Konstanz erforderlich? – Ation

+1

Um es ROMable machen, so dass es in nichtflüchtigem Speicher, nicht im RAM platziert werden würde. Dies ist sehr wichtig für das Thema ... –

+1

In Bezug auf die Makrolösung - konnten Sie nicht den Namen des Speicherobjekts als Argument für das Makro übergeben? –

Antwort

2

Sie einen Zeiger mit einer ganzen Zahl ersetzen kann, und eine einzelne statische Array für alle DataNonConst Objekte erstellen, wie folgt aus:

class DataNonConst { 
    uint8_t byte; 
    bool valid; 
}; 

static DataNonConst storages[MAX_DATA]; 

class DataConst { 
    const char filename[128]; 
    const int dataIndex; 
    DataNonConst *data() { 
     return &storages[dataIndex]; 
    } 
}; 

const DataConst holderOne("first_long_name_here", 0); 
const DataConst holderTwo("second_long_name_here", 1); 
const DataConst holderThree("third_long_name_here", 2); 

Dieser Ansatz der Flyweight Pattern inspiriert ist, obwohl es für einen anderen verwendet wird offensichtlich Zweck hier.

Ein offensichtlicher Nachteil ist, dass Sie Ihre Einträge manuell zählen müssen, um Doppelungen zu vermeiden. Allerdings gibt es nur ein storages Objekt, also gibt es nichts anderes zu erstellen.

+0

Wie ist das besser, als einen Zeiger auf die DataNonConst im DataConst-Objekt zu haben? Sie müssen noch Flash berühren, um zu bekommen, was ist in ram. – dspeyer

+0

@ dspeyer - Zugriff auf zugrunde liegenden Speicher sollte durch const Halter Objekt gehen, die in Flash ist - nichts falsch über das –

0

Erklären Sie einfach die const Mitglieder static const.

static Mitglieder werden in jedem Fall getrennt von den Objektinstanzdaten gespeichert. Ihr Compiler kann automatisch static const solche Member in ROM platzieren, aber wenn nicht, gibt es wahrscheinlich Compiler-Direktiven, die das erzwingen.

+1

const Elemente für jedes Split-Objekt müssen unterschiedlich sein, so kann es nicht statisch sein const. –

+0

@FreddieChopin: Die Tatsache bleibt, dass nicht-statische const-Member aufgrund der Semantik von "const" in C++, in denen const-Member Runtime-initialisiert werden, nicht im ROM liegen können.Sie könnten mehrere Klassen verwenden, die nur statische const-Datenelemente enthalten und Klassen ableiten von ihnen als notwendig mit den nicht-const Daten in der Unterklasse. – Clifford

2

Soweit ich sehe, ist das einzige Problem hier, dass Ihr "Handle" -Objekt eine char[] Mitglied hat. Ich wette, wenn Sie das durch eine const char* ersetzen, löst das Ihre Probleme, da String-Literale wahrscheinlich sowieso im nichtflüchtigen Speicher sind.

class Data 
{ 
    const char* filename; 
    uint8_t byte; 
    bool valid; 
    Data(const char* filename_) : filename(filename_), valid(false) {} 
    uint8_t read() 
    { 
     if (!valid) 
      performReallySlowRead(filename, &byte); 
     valid = true; 
     return byte; 
    }; 
    void write(uint8_t new_value) 
    { 
     byte = new_value; 
     performRealWriteToMedia(filename, byte); 
     valid = true; 
    }; 
}; 

Data holder("some_long_name_here"); //name is in the non-volatile memory anyway 

char buffer[128] = "\var\bin\omg"; 
Data holder2(buffer); //runtime names work fine, but have to manage the storage yourself. 

Oder, wenn Sie schlau sein wollen, können Sie dies eine Vorlage machen und sogar die Menge an Speicher (auf Kosten von etwas zu sein sparen mehr verwirrend. Dies hat den zusätzlichen Vorteil, in der Lage zu fügen Sie einfach static Mitglieder hinzu, die auch in nichtflüchtigem Speicher sind, aber nicht gemeinsam pro Dateiname.Es ist nicht offensichtlich, aber diese kann mit Dateien verwendet werden, deren Namen zur Laufzeit nicht bekannt sind, aber ich empfehle das nicht .

template<const char* filename> 
class Data 
{ 
    uint8_t byte; 
    bool valid; 
    uint8_t read() 
    { 
     if (!valid) 
      performReallySlowRead(filename, &byte); 
     valid = true; 
     return byte; 
    }; 
    void write(uint8_t new_value) 
    { 
     byte = new_value; 
     performRealWriteToMedia(filename, byte); 
     valid = true; 
    }; 
}; 
Data<"some_long_name_here"> holder; 

extern char buffer[128] = "\var\bin\omg"; 
A<buffer> holder2; //this always refers to the filename currently in buffer 

Allerdings, wenn Sie wirklich möchte ein Split-Objekt, Ihr was Sie als Split-Vorschlag haben, ist wirklich die beste Antwort, die ich mir vorstellen kann.

+0

unglücklicherweise ist der "Dateiname" nur ein Beispiel (; In Ihrer Lösung der Zeiger auf Dateiname wird im RAM gespeichert, aber für diese Diskussion nehmen wir an, dass jedes Byte des Speichers zählt, deshalb muss es geteilt werden, und deshalb zeigt die Konstante auf nicht-konstanten Teil und nicht umgekehrt. –

+0

@FreddieChopin: Ja, in meinem ersten Beispiel ist der Zeiger auf den Dateinamen im RAM. Wenn jedes Byte zählt, verwenden Sie das Vorlagenbeispiel. Das hält nur die benötigten Bits im RAM. –

+0

Ihre Antwort ließ mich über Umzugssemantik und Rvalue-Referenzen von C++ 11 nachdenken. Ist so etwas möglich? Zum Beispiel würde der Konstruktor für DataConst eine rvalue-Referenz zu DataNonConst nehmen: DataNonConst holder (DataConst()); wäre so etwas möglich? –

0

Wenn Sie bereit sind, eine maximale Anzahl von Objekten haben, können Sie Indizes als implizite Zeiger verwenden:

class Data; 
Data *dataArray; 
char **filenameArray; 

class Data { 
    uint8_t byte; 
    bool valid; 
    char*& filename() { 
    int index = this - dataArray; 
    return filenameArray[index]; 
    } 
public: 
    byte read() { 
    if (!valid) 
     performReallySlowRead(filename(), &byte); 
    valid = true; 
    return byte; 
    } 
    init(char* fn) { 
    filename() = allocateInFlash(strlen(fn)); 
    strdup(filename(), fn); 
    valid = FALSE; 
    } 
}; 

// Somewhere before things get run 
dataArray = allocateInRam(sizeof(Data)*nDatas); 
filenameArray = allocateInFlash(sizeof(char*)*nDatas); 

Das heißt, Sie brauchen keine Overhead, und Sie brauchen nicht zu berühren Flash zum Lesen von RAM. Sie verwenden auch nicht mehr die Standardspeicherverwaltung, was ein Plus oder ein Minus sein könnte.

0

Wenn Sie bereit sind, etwas rein Flash-Overhead haben, könnten Sie tun:

class Data; 
std::map<Data*,string, less<Data*>, FlashAllocator<pair<Data*,string>> fns; 
class Data { 
    uint8_t byte; 
    bool valid; 
public: 
    byte read(){ 
    if (!valid) 
     performReallySlowRead(fns[this],&byte); 
    valid = true; 
    return byte; 
    } 
    Data(string fn) { 
    fns[this] = fn; 
    } 
    ~Data() { 
    fns.erase(this); 
    } 
}; 

Karten nicht trivialen Kopf haben, aber es ist alles in Flash.

+0

Sehe ich richtig, dass Flash zur Laufzeit schreibbar sein müsste? –

+0

@FreddieChopin Ja. Flash ist im Allgemeinen, oder? Wenn nicht, woher kommen dann die Dateinamen? – dspeyer

+1

wenn Sie denken, "Flash" als SD-Karte dann sicher - es ist beschreibbar, aber hier "flash" == "Code-Nur-Lese-Speicher". Es ist auch beschreibbar, aber das ist nicht so einfach - die Idee besteht darin, die Objekte zur Kompilierzeit zu erstellen. –

1

Mit einigen Einschränkungen können Sie Vorlagen verwenden, um eine statische Instanz von DataNonConst zu erstellen, auf die von jedem ROMable DataConst-Objekt verwiesen wird. Einschränkungen

:

  • Der Code verwendet die __LINE__ Makro- und so muss es nur sein, eine DataConst Erklärung pro Zeile. Wenn alternativ das Makro verfügbar ist, kann es anstelle von __LINE__ verwendet werden, um mehrere Deklarationen pro Zeile zuzulassen.

  • Die Objekte DataNonConst werden immer auf Null initialisiert.

Code:

struct DataNonConst 
{ 
    uint8_t byte; 
    bool valid; 
}; 

struct DataConst 
{ 
    const char filename[128]; 
    DataNonConst * const data; 
}; 

namespace { 

template <long n> 
struct DataNonConstHolder 
{ 
    static DataNonConst data; 
}; 

template <long n> DataNonConst DataNonConstHolder<n>::data; 

} 

// If __COUNT__ macro is supported, use it instead of __LINE__ 
#define Initializer(s) { s, &DataNonConstHolder<__LINE__>::data } 

const DataConst dc1 = Initializer("filename1"); 
const DataConst dc2 = Initializer("filename2"); 
Verwandte Themen