Forum: PC-Programmierung std::string ohne dynamische allocation


von Vlad T. (vlad_tepesch)


Lesenswert?

Hi

Ich würde für ein embedded projekt ganz gerne c++ einsetzen.
Allerdings will ich auf dynamische speicherverwaltung verzichten.

sprich container basierend auf einem statischen array der 
Templatevariablen.
und vor allem würde ich gerne einen Teil der strign funktionalität 
nutzen.
Allerdings dürfte beim Konstruieren eines Stringobjektes nichts 
dynamisch alloziert oder kopiert werden, sondern der string sollte als 
puffer direkt den übergebenen char Puffer benutzen.

HAt jemand schon mal sowas gemacht?
gibt es eine (freie) Abwandlung der STL speziell für solche embedded 
anforderungen?

Klar, das ist alles kein Hexenwerk, aber warum das Rad zwei mal 
erfinden.

Gruß
Vlad

von vfcfsdfs (Gast)


Lesenswert?

schau dir mal placement-new an, vielleicht hilft dir das ein bisschen 
weiter.

von vfcfsdfs (Gast)


Lesenswert?

achso und schau dir an, wie man mittels Templates fixe Größen übergeben 
kann.
Also sowas wie MeinString<30> meinString; wäre möglich, und mit dem Wert 
30 setzt du im Template dann ein statisches Array.

template<size_t max>
class xy
{
...
unsigned char[max] array;
...
};

Ich weiß, ist wieder zum Selberbauen, fertige Lösung weiß ich leider 
nicht.

von Ist so (Gast)


Lesenswert?

Der Aufwand das mit der STL hinzubekommen steht in keinem Vergleich zu 
den wenigen Vorteilen die man dadurch gewinnt. Die STL ist schon von 
ihrer Architektur ziemlich ungeeignet für Embedded-Software in kleinen 
Mikrocontrollern. C++ selber ist in der Umgebung auch nicht das Wahre, 
und degeneriert schlussendlich meist zu C mit ein bisschen Dekoration. 
Da kann man auch direkt C nehmen, wenn man auf die wirklich 
interessanten C++-Eigenschaften verzichten muss.

Um es anders auszudrücken, ich bin immer wieder erstaunt, wie viel 
Aufwand Leute betreiben wollen um etwas nicht zu lernen. In diesem Fall 
ein paar Strings von Hand in C (nicht C++) mit Pointern zu handhaben.

Der Schlüssel die Speicherverwaltung von string zu ändern ist auch nicht 
Placement-new alleine, sondern auch ein allocator. Zumindest 
theoretisch. In der Praxis wird das Allocator-Argument gerne ignoriert. 
Dann steht man richtig blöd da.

von Vlad T. (vlad_tepesch)


Lesenswert?

vfcfsdfs schrieb:
> schau dir mal placement-new an, vielleicht hilft dir das ein bisschen
> weiter.

das hilft an der Stelle ja nicht.
das Placement new legt ja nur die Struktur in den vorbereiteten 
Speicher.

String funktioniert ja eher so:
1
class String
2
{
3
  public:
4
   String(const char* input)
5
   {
6
     size = strlen(input);
7
     capacity = size+1;
8
     buffer = new char[capacity];
9
     strcpy(buffer, input)
10
   }
11
   ~String(){
12
     delete[] buffer;
13
   }
14
15
  private:
16
   int size;
17
   int capacity;
18
   char* buffer;
19
}

Mach ich ketzt ein placement new:
1
  static String strBuffers[10];
2
  String* s = new(&strBuffers[5]) String(charbuffer);

wird der String zwar in den Statischen Puffer construiert (also dessen 
Members size, capacity und der buffer-Pointer), aber der eigentliche 
Zeichenkettenpuffer ist nachwievor dynamisch per new allozierter 
Speicher.

Der String würde sowieso auf dem Stack landen.

es gibt ja dann noch die allocatoren, den man dem String als template 
paraemter übergeben kann. aber so ganz das, was ich will ist das auch 
nicht.

Eigentlich bräuchte man eine Wrapper-Klasse, die weitestgehend 
kompatibel zu std::String ist, aber nur auf dem übergebenen Puffer 
arbeitet, also keine Operationen ausführt, die die maximale länge 
überschreiten.

Das Gleiche bei den containern.

vfcfsdfs schrieb:
> achso und schau dir an, wie man mittels Templates fixe Größen übergeben
> kann.

das möchte ich ja auch nicht. Dann habe ich ja für jede Pufferlänge den 
Template-Code instanziiert. Die Länge selbst kann schon ein 
Laufzeitparameter sein.

Das man das selbst schreiben kann ist keine Frage, aber das 
STL-Kompatibel hinzubekommen ist schon eine ziemliche Herausfoderung (zb 
richtige Funktionsweise der Iteratoren, damit auch STL-Hilfsfunktionen 
funktionieren)

von Vlad T. (vlad_tepesch)


Lesenswert?

Ist so schrieb:
> C++ selber ist in der Umgebung auch nicht das Wahre,
> und degeneriert schlussendlich meist zu C mit ein bisschen Dekoration.
Alleine schon wegen diesem bisschen Dekoration lohnt sich die Verwendung 
von C++

> Da kann man auch direkt C nehmen, wenn man auf die wirklich
> interessanten C++-Eigenschaften verzichten muss.
Aber lieber ein bisschen davon, als in Plain-C umständlich die Konzepte, 
die man problemlos nutzen könnte, nachzuprogrammieren.
Und genau hier, weiß ich wovon ich rede - das kannst du mir glauben.

>
> Um es anders auszudrücken, ich bin immer wieder erstaunt, wie viel
> Aufwand Leute betreiben wollen um etwas nicht zu lernen. In diesem Fall
> ein paar Strings von Hand in C (nicht C++) mit Pointern zu handhaben.
Da du micht nicht kenntst, maßt du dir hier ganz schön was an.


>
> Der Schlüssel die Speicherverwaltung von string zu ändern ist auch nicht
> Placement-new alleine,
wie ich schon (scheinbar zeitgleich mit dir) schrieb, ist das placement 
new in dem Fall überhaupt keine Hilfe.

> sondern auch ein allocator. Zumindest
> theoretisch. In der Praxis wird das Allocator-Argument gerne ignoriert.
> Dann steht man richtig blöd da.
Das Allocator element war mir bisher leider auch noch nie eine Hilfe.
Die Bedingung der Austauschbarkeit verschiedner Allocator Instanzen hat 
bisher immer gestört

von Rene H. (Gast)


Lesenswert?

Hey Vlad,

ich würde am ehesten den Ansatz mit eigenem Allocator versuchen. Mit 
maps und vektors habe ich das schon gemacht um diese im shared memory 
ablegen zu können.

Wie sich das bei std::string verhält weiss ich nicht, ist aber zu 
untersuchen. Wenn ich morgen an meiner Kiste bin werde ich mir das mal 
ansehen, ist es doch ein knackiges Problem.

Grüsse,
René

von Vlad T. (vlad_tepesch)


Lesenswert?

Mit allocator wird das nicht funktionieren, da der Anwendungszweck ein 
anderer ist.

Ein Allokator versorgt alle Strings, die mit diesem Allokator als 
Templateparameter erzeugt wurden mit Speicher.

Das heißt, wenn ich einen AllokatorX schreibe und zwei
String<xhar, AllokatorX >  s1;
String<xhar, AllokatorX >  s2;
habe habe ich quasi trotdem nur einen Allokator, bzw, die instanzen der 
beiden Allokatoren, die in den Strings enthalten sind müssen 
austauschbar sein.
Allokatoren sind also quasi Pseudo-Singeltons. Ich hab zwar mehrere 
Instanzen, aber egal welche instanz ich mir nehme, sie mus sich genauso 
verhalten, als hätte ich die andere benutzt.

Das heißt man hat irgendwo einen großen Puffer, aus denen sich alle 
String<xhar, AllokatorX > dynmisch bedienen, wenn sich ihre größe 
ändert. Der allokator muss also da sich darum kümmern, dass er eine 
Fragmentierung minimiert.

Was ich möchte ist ja quasi mur ein Wrapper um ein C-Array, was irgendwo 
statisch ist mit einer maximalen Größe. Also quasi eine
std::string-Abart, der man im Konstruktur den Speicher und die Größe 
gibt und die dann ganz normal darauf arbeitet, nur eben nicht bei Bedarf 
neuen speicher anfordert.

von Rene H. (Gast)


Lesenswert?

Hmm.... Das verstehe ich nicht. Was hindert Dich daran zwei verschiedene 
Allokatoren zu nehmen? Der Interoperabilität tut sich deswegen nichts.

von Rene H. (Gast)


Lesenswert?

Und wenn Du eine eigene allocator Methode definierst ist auch mit 
reallocation nichts. Das heisst Du hast den allozierten Speicher im 
Griff.

von Ist so (Gast)


Lesenswert?

Vlad Tepesch schrieb:
> Ist so schrieb:
>> C++ selber ist in der Umgebung auch nicht das Wahre,
>> und degeneriert schlussendlich meist zu C mit ein bisschen Dekoration.
> Alleine schon wegen diesem bisschen Dekoration lohnt sich die Verwendung
> von C++

Aus praktischer Erfahrung kann ich dir sagen es lohnt nicht. Man landet 
bei so vielen Konventionen "benutz das nicht, benutze jenes nur so", die 
man dann konsequent einhalten muss, damit das was wird, dass es zum 
Schluss keinen Spaß mehr hat. Hinzu kommt, dass man sich die 
Maintenance-Probleme von morgen einbaut, da man morgen schon nicht mehr 
weiß, warum man welche Konventionen warum eingeführt hat.

Aber Hauptsache, man hat seine Lieblingssprache mit dem Vorschlaghammer 
für etwas passend gemacht, für das sie nicht wirklich passt.

> Und genau hier, weiß ich wovon ich rede - das kannst du mir glauben.

Das glaube ich dir nicht. Dann müsstest du nicht nach einer 
String-Bibliothek fragen und dich nicht mit Typen wie mir abgeben. Dann 
hättest du dir mit all deiner Erfahrung still und heimlich, ruckzuck 
einfach was gebastelt, schließlich weißt du wovon du redest.

von Klaus W. (mfgkw)


Lesenswert?

vfcfsdfs schrieb:
> achso und schau dir an, wie man mittels Templates fixe Größen übergeben
> kann.
> Also sowas wie MeinString<30> meinString; wäre möglich, und mit dem Wert
> 30 setzt du im Template dann ein statisches Array.

Das ist aber etwas ungeschickt, weil jeder Wert für den 
template-Parameter einen neuen Typ erzeugt.
D.h. MeinString<30>, MeinString<31>, MeinString<32> etc. sind jeweils 
eigene Datentypen; für jeden benutzten wird Code erzeugt.

Möchte man einen Typ haben und trotzdem verschiedene Längen für die 
Objekte haben, ist es besser, die Länge im ctor anzugeben.

von Vlad T. (vlad_tepesch)


Lesenswert?

Rene H. schrieb:
> Hmm.... Das verstehe ich nicht. Was hindert Dich daran zwei verschiedene
> Allokatoren zu nehmen? Der Interoperabilität tut sich deswegen nichts.
duplizierter Code, da für jeden Allokatortyp ein neuer string typ 
erzeugt wird, die zudem inkompatibel untereinander sind.

Rene H. schrieb:
> Und wenn Du eine eigene allocator Methode definierst ist auch mit
> reallocation nichts. Das heisst Du hast den allozierten Speicher im
> Griff.
das schon, aber der String selbst fordert ja trotzdem munter Speicher in 
unterschiedlichsten Größen an und kopiert den im C'tor übergebenen 
String in diesen Puffer.

von Vlad T. (vlad_tepesch)


Lesenswert?

Ist so schrieb:
> Aber Hauptsache, man hat seine Lieblingssprache mit dem Vorschlaghammer
> für etwas passend gemacht, für das sie nicht wirklich passt.
Das Argument hätte ich gelten lassen, wenn ich nach Visual Basic, Java 
oder C# gefragt hätte, aber so ist es einfach nur lächerlich.

Ist so schrieb:
> Aus praktischer Erfahrung kann ich dir sagen es lohnt nicht.
Dann lehrte uns unsere Erfahrung unterschiedliche Sachen.
Meine Erfahrung mit großen C-Projekten (mehrere 100 Files und mehrere 
10.000 loc) hat mir gezeigt, dass das C++ häufig sehr zur Lesbarkeit des 
Codes beigetragen hätte.
Alleine durch Namespaces und Memberfunktionen und Constanten/Enums im 
Class-Scope herrscht vielmehr Ordnung im globalen Namensraum. Allein 
deswegen lohnt es sich.
Wenn man auf Features wie Polymorphie angewiesen ist, wird C sogar zum 
Graus, wenn eine Funktion auf einmal virtuell werden soll, passt man 
100e Stellen Code an (ob automatisiert oder von Hand sei einmal 
dahingestellt), weil man die Funktion in die VTable packen muss und alle 
bisherigen normalen Funktionsaufrufe durch VTable-Funktionspointer-Calls 
ersetzen muss.

> Man landet
> bei so vielen Konventionen "benutz das nicht, benutze jenes nur so", die
> man dann konsequent einhalten muss, damit das was wird, dass es zum
> Schluss keinen Spaß mehr hat.
was ist so schwer daran sich ein subset an c++ zu definieren, was man 
benutzen kann? Als grobe Regel: alles was dynamisch Speicher holt ist 
nicht erlaubt. RTTI ist nicht erlaubt.

> da man morgen schon nicht mehr
> weiß, warum man welche Konventionen warum eingeführt hat.
Du weißt wofür es eine Dokumentation gibt?

Ist so schrieb:
>> Und genau hier, weiß ich wovon ich rede - das kannst du mir glauben.
ups, hier ist das komma aber ganz schön verrutscht

> Das glaube ich dir nicht. Dann müsstest du nicht nach einer
> String-Bibliothek fragen und dich nicht mit Typen wie mir abgeben. Dann
> hättest du dir mit all deiner Erfahrung still und heimlich, ruckzuck
> einfach was gebastelt, schließlich weißt du wovon du redest.

Natürlich kann man das selbst schreiben, aber erstens ging es mir nicht 
nur um eine String-Bibliothek, sondern eher eine Art embedded-STL, 
zweitens ist schon vorhanden und getestet immer besser als 
selbstgeschrieben, drittens: machst du das bei deinem Arbeitgeber auch 
so? "Ich hab nicht geschaut, ob das schonmal jemand gemacht hat. Ich bin 
so der C-Checker ich mach das lieber selbst komplett neu"

Deshalb zitiere ich mein Eingangspost mal:
Vlad Tepesch schrieb:
> Klar, das ist alles kein Hexenwerk, aber warum das Rad zwei mal
> erfinden.

Außerdem, um diese Sinnlosdiskussion (@rene: das ist nicht an dich 
gerichtet) zu beenden:

Die Frage war:
Vlad Tepesch schrieb:
> HAt jemand schon mal sowas gemacht?
> gibt es eine (freie) Abwandlung der STL speziell für solche embedded
> anforderungen?

und nicht, "Ist eure Lieblingssprache C oder C++?" oder "Was denkt ihr 
von jemanden, der nicht alles selbst schreibt, sondern erst mal nach 
verfügbaren Lösungen schaut."

von Karl H. (kbuchegg)


Lesenswert?

Ist so schrieb:
> Vlad Tepesch schrieb:
>> Ist so schrieb:
>>> C++ selber ist in der Umgebung auch nicht das Wahre,
>>> und degeneriert schlussendlich meist zu C mit ein bisschen Dekoration.
>> Alleine schon wegen diesem bisschen Dekoration lohnt sich die Verwendung
>> von C++
>
> Aus praktischer Erfahrung kann ich dir sagen es lohnt nicht.

Das kann man aber so nicht stehen lassen.
Was sind 2 der Hauptprobleme in C
* Arrays wissen nicht wie groß sie sind
* Strings sind kein eigener Datentyp sondern beruhen
  auf einer Konvention

Und gerade in diesen beiden Bereichen kann dir C++ schon enorm unter die 
Arme greifen, wenn es darum geht robusten Code zu produzieren. Eben 
genau deshalb, weil du die Möglichkeit hast, die Konventionen, bzw. was 
viel wichtiger ist, das Einhalten dieser Konventionen, in einer Klasse 
zu sammeln und so zu verpacken, dass ein Verwender der Klasse nicht mehr 
dagegen verstossen kann. Ersetze ich 5 einzuhaltende Konventionen durch 
lediglich 1 an die ich mich halten muss, dann habe ich dadurch etwas 
gewonnen.
Ein Verwender muss sich nach wie vor einiger Dinge bewusst sein, das 
gestehe ich dir gerne zu. Aber zb den Schlendrian des 'Fehlerbehandlung 
kommt später' kannst du mit einer Klasse wirkungsvoll abstellen. Und 
genau an diesem Punkt kranken viele C Programme (inklusive meiner 
eigenen).

von dfsdfs (Gast)


Lesenswert?

Dann reservier am Anfang des Programms halt einen Speicherblock mit 
unsigned char mem[BLOCK_LEN] und verwalte den Speicher dort selbst.

Jeder neue String bekommt dann x byte fix von dem Block zugewiesen, ab 
einem gewissen Offset. Die Operatoren, die man so kennt, wie operator= 
oder operator+= greifen dann halt auf den zugewiesenen Speicher zu.
In einer statischen Variable merkst du dir, welche Memory Blöcke schon 
belegt sind. Im CTOR werden die Blöcke belegt, im DTOR freigegeben.
Der Einfachheit halber bekommt jeder String gleich viel Speicher (z.B. 
50byte), das verhindert auch die Fragmentierung.

Klar, sehr flexibel ist das ganze nicht, aber du kommst ohne new/delete 
aus und bist sehr flott weil du nur mit statischen 
Arrays+Indizes/Offsets arbeitest. Außerdem kannst du die Klasse ja sehr 
gut an deine Anwendung anpassen.

Also so in der Art:

[...............][...............][...............] usw...
   String 1        nicht belegt        String 2

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.