Speicherverbrauch shared_ptr und normaler *
-
Ja die Add Methode ist aber nicht const&
void Add(shared_ptr<TcAdsSymbolInfo> symbolInfo)
-
Wie verhalten sich das Programm+Log, wenn du
static symbol_count = 0; PROCESS_MEMORY_COUNTERS mem_info; GetProcessMemoryInfo(GetCurrentProcess(), &mem_info, sizeof(mem_info)); TRACE((to_string(++symbol_count) + " " + to_string(mem_info.WorkingSetSize)).c_str());
vor dem return in CreateSubSymbols einfügst?
Wieviel Speicher wird per Symbol im Schnitt benötigt?
Ist der Speicherverbrauch pro Symbol (einigermaßen) konstant oder wächst er mit der Zeit?
-
@camper
Habe das mit dem Visual Studio memoery profiler schon getestet.
da hat jedes Symbol zwischen 0,6 - 3 K Byte
das mal 1,4 Millionen Objekte kommt schon etwa hin mit den 2 GB.Aber ich teste das mit deinem Code schnipsel auch noch bevor wieder...
-
Ich meinte weniger den Aufruf von der Add Methode sonder eher das hinzufügen zur Map. Aber das ist ja im Moment auch egal. Wenn da was schief läuft, fliegt dir die Software zur Laufzeit irgendwann um die Ohren und reserviert nicht 2 GB Speicher.
Hast du mal Valgrind, oder eine entsprechende alternative laufen lassen. Bei dem externen Code, den du gezeigt hast, bin ich mir nicht sicher, dass da alles sauber läuft.
-
Ja bei dem externen Code bin ich mir auch nicht sicher. Der sieht wirklich graunhaft aus.
Aber ich weiß noch nicht ob der mein Speicher verbraucht.
Und wieso der Code nur 600KB braucht um die die 1,4 Millionen Datensätze auszulesen ist auch noch nicht ganz klar.
-
booster schrieb:
@hustbear
Bin unter Windows 7 und hier ist die Spalte Arbeitsspeicher (privater Arbeitssatz) eingeblendet!Ja. Das ist nicht die Spalte die du suchst. Die kann sich quasi beliebig ändern.
Was du suchst ist "commit size". Ich weiss aber nicht wie die im Windows 7 Task Manager heisst. Und auch dann gibt's Pitfalls, nämlich dass der Heap freigegebenen Speicher nicht unbedingt ans System zurück gibt (bzw. das oft auch gar nicht kann), und dass AFAIK Programmcode sowie geladene DLLs mitgezählt werden.
-
@hustbear
Würde mal sagen die commit size ist die "Zugesicherte Größe".
-
Wie verhalten sich das Programm+Log, wenn du ... vor dem return in CreateSubSymbols einfügst?
Also ich versuch mal zu interpretieren und hoffe ich bekomme nicht gleich wieder eine auf die Mütze, wie "ist ja alles inkonsistent" und du denkst ja gar nicht nach".
Folgende Erkenntnise.
WorkingSetSize nach 1. Aufruf von CreateSubSymbol:
34631680 BytesWorkingSetSize nach 500. Aufruf von CreateSubSymbol:
35221504 BytesUnterschied -> 589824 Bytes = 589KB
Sprich nach 1,4 Millionen Symbolen
kommt dann schon um die 2GB raus.
-
Also um noch die beiden Fragen zu beantworten
Wieviel Speicher wird per Symbol im Schnitt benötigt?
Also wenn ich noch weiterlaufen lasse und mehr als 500 Symbole betrachte um die
1,5 KBIst der Speicherverbrauch pro Symbol (einigermaßen) konstant oder wächst er mit der Zeit?
Er ist einigermaßen konstant. Wächst auf jeden Fall nicht mit der Zeit.
-
Wieviele von den 1,4 Millionen Symbolen sind Array-Elemente (der Datenblock enthält nur Informationen über Arraydimensionen, das Programm erzeugt aber für jedes einzelne Array-Element einen Symboleintrag) ?
Ich habe ja keine Ahnung, was mit den Daten später gemacht werden soll, allerdings erscheint es mir relativ sinnlos, für jedes einzelne Element eines Arrays Informationen zu speichern, die sowieso identisch mit dem übergeordneten Symbol sind. Das würde auch erklären, wie ein 682KB großer Datenblock sich überhaupt auf so viele Elemente beziehen kann.
-
Hi camper.
Du bist aktuell so weit wie ich :-).
Dies gilt aber nicht nur für Array -Elemente sondern auch für die Instanzen von Strukturen.
Die Daten werden dazu gebraucht um Zugriffsfunktionalität herzustellen um auf bestimmte Variablen der SPS direkt zugreifen zu können.
Ok also praktisches Beipiel:
Ich bekomme eine Liste von Variablennamen.
z.B"Main.FunctionBlockComplex.FirstStruct.States[5].ErrorState"
Zu dieser Variable muss ich nun die benötigten Informationen (Indexgroup, IndexOffest, Size, DataType usw.) einholen um dann später diese Variable direkt lesen bzw. schreiben zu können.
Jetzt würde die Möglichkeit bestehen, nur die Variablendeklaration dieser einen Variablen auszulesen. Da es aber nicht nur eine Variable ist von der man die Informationen benötigt war die Idee alle Variablendeklarationen auf einmal auszulesen. (um nur einmal die SPS zu kontaktieren) Diese in einer Struktur zu speichern um schnellen Zugriff auf die Variablendeklartionen zu haben.
Nach dem ich mir dann meine eigenen Klasseninstanzen der benötigten Variablen erzeugt habe kann ich diese "Lookup Tabelle" wieder löschen.Das hat bisher wie gesagt auch sehr gut funktioniert. Die Lookup Tabelle wird ja nur einmal ab Start erzeugt und danach wieder frei gegeben.
-
booster schrieb:
"Main.FunctionBlockComplex.FirstStruct.States[5].ErrorState
Die Information, die für Arrayelemente gespeichert wird, ist für alle Elemente identisch. Es ist völlig absurd, die absolut gleiche Information mehrfach zu speichern, solange nur das Array groß genug ist, wird dir immer am Ende der Speicher ausgehen, unabhängig von allen anderen Versuchen, möglichst wenig Speicher zu verbruachen.
Wie wäre es mit folgender Änderung
UINT CAdsParseSymbols::SubSymbolCount(PAdsDatatypeEntry pEntry) ... else if ( pEntry->arrayDim ) { cnt = 1; } ... BOOL CAdsParseSymbols::SubSymbolInfo(CAdsSymbolInfo main, UINT sub, CAdsSymbolInfo& info) ... else if ( pEntry->arrayDim ) { // store one generic element representing any array elements info.iGrp = main.iGrp; info.iOffs = main.iOffs; info.size = baseSize; info.dataType = pEntry->dataType; info.flags = pEntry->flags; info.type = PADSDATATYPETYPE(pEntry); info.comment = PADSDATATYPECOMMENT(pEntry); info.name = main.name + "[]"; info.fullname = main.fullname + "[]"; return TRUE; } ...
Bevor jetzt ein Lookup durchgeführt wird, muss nur zunächst geprüft werden, ob auf ein Arrayelement zugegriffen wird (name enthält Indexinformationen zwischen "[]"), dann muss dieser name entsprechend angepasst werden, indem die Indexinformationen gelöscht werden. Im Anschluss ggf. noch überprüfen, ob der Index innerhalb des Arrays liegt.
-
Wenn ich den Code richtig lese, haben Arrayelemente nie selbst Subelemente.
Also die haben schon Subelemente aber jeder Index halt die selben.
Also ich kann ja ein Array mit Structs haben.Und die Information, die für Arrayelemente gespeichert wird, ist für alle Elemente identisch.
Für das Oberste Element dann schon. Bis auf den IndexOffset der ist bei jedem Element anderes.
Den muss ich dann halt noch berechnen. Also Offset von Array selbst + Arrayindex * Sizeof(Array Datatype)
-
booster schrieb:
Ich bekomme eine Liste von Variablennamen.
z.B"Main.FunctionBlockComplex.FirstStruct.States[5].ErrorState"
Zu dieser Variable muss ich nun die benötigten Informationen (Indexgroup, IndexOffest, Size, DataType usw.) einholen um dann später diese Variable direkt lesen bzw. schreiben zu können.
Jetzt würde die Möglichkeit bestehen, nur die Variablendeklaration dieser einen Variablen auszulesen. Da es aber nicht nur eine Variable ist von der man die Informationen benötigt war die Idee alle Variablendeklarationen auf einmal auszulesen. (um nur einmal die SPS zu kontaktieren) Diese in einer Struktur zu speichern um schnellen Zugriff auf die Variablendeklartionen zu haben.
Nach dem ich mir dann meine eigenen Klasseninstanzen der benötigten Variablen erzeugt habe kann ich diese "Lookup Tabelle" wieder löschen.Mir scheint dein Algorithmus arbeitet genau falschherum. Da du bereits eine vollständige Liste aller gesuchten Variablen hast, wäre es doch sinnvoller, beim Iterieren über die SPS-Daten direkt nur die Daten zu kopieren, die benötigt werden, anstatt erst eine umständliche Datenstruktur aller potentiell möglichen Variablen zu erstellen. Damit wird gar nicht erst unnötiger Speicher für Symbole verbraucht, die sowieso am Ende nicht benötigt werden, ausserdem kann der Parser von vornherein Subsymbole übergehen, die nicht benötigt werden.
Die Aufgabe besteht also sinngemäß darin eine Funktion
void update_from_sps_data(std::vector<ITcAdsSymbol>& vars, const vector<char>& sym_data, const vector<char>& dt_data)
zu schreiben, wobei eingangs in vars die Namen der gesuchten Variablen bereits initialisiert sind, und die Funktion die restlichen Informationen der einzelnen vars-Elemente anhand der übergebenen Rohdaten aktualisiert?In dem Fall solltest du wahrscheinlich den Beispielcode wegwerfen und neuschreiben.
Wenn du ein paar Beispieldaten zum runterladen und testen bereitstelltst, versuche ich mich evtl. auch selbst daran.
-
Compiliert (g++/clang++ unter Linux, deshalb die paar Zeilen am Anfang) als C++17, aber nat. völlig ungetest:
Fast keine dynamische Allokation (lediglich zwei kleine Vektoren und die string-Zuweisungen am Ende).#ifndef __MSC_VER #include <cstddef> using __int64 = long long; using BOOL = int; using PCHAR = char*; #define __stdcall #define __declspec(x) #endif #include "TcAdsDef.h" // https://raw.githubusercontent.com/bgstaal/ofxBeckhoffADS/master/include/TcAdsDef.h ////////////////////////////////////////////////////// #include <algorithm> #include <cstddef> #include <cstdint> #include <cstring> #include <iostream> #include <string> #include <string_view> #include <stdexcept> #include <type_traits> #include <vector> using namespace std::literals::string_view_literals; enum class AdsDatatypeId; struct ITcAdsSymbol { std::string name; std::string shortName; std::string type; std::string comment; decltype(AdsSymbolEntry::size) size; decltype(AdsSymbolEntry::iGroup) indexGroup; decltype(AdsSymbolEntry::iOffs) indexOffset; AdsDatatypeId datatype; //enum }; template <typename T, std::enable_if_t<std::is_trivial_v<T>, int> = 0> T read_raw(const char* p) { T res; std::memcpy(&res, p, sizeof res); return res; } std::size_t checked_sum(std::initializer_list<std::size_t> args) { std::size_t result = 0; for ( auto arg : args ) { auto sum = result + arg; if ( sum < result ) throw std::runtime_error("overflow"); result = sum; } return result; } class array_info { using size_type = decltype(AdsSymbolEntry::iOffs); public: array_info(const char* dim_data, size_type dims, size_type size) noexcept : dim_data(dim_data), dims(dims), size(size) {} array_info(const array_info&) noexcept = default; array_info(array_info&&) noexcept = default; array_info& operator=(const array_info&) noexcept = default; array_info& operator=(array_info&&) noexcept = default; ~array_info() noexcept = default; auto elem_info(std::string_view index) const { size_type real_index = 0; auto elem_size = size; for ( size_type i = 0; i != dims; ++i ) { auto pos = index.find(','); if ( ( i != dims - 1 ) == ( pos == index.npos ) ) throw std::runtime_error("index with wrong number of dimensions"); auto cur_index = index; if ( pos != index.npos ) { cur_index.remove_suffix(cur_index.size() - pos); index.remove_prefix(pos + 1); } auto n = svtoi(cur_index); auto info = read_raw<AdsDatatypeArrayInfo>(dim_data + i * sizeof(AdsDatatypeArrayInfo)); if ( n < info.lBound || n >= info.lBound + info.elements ) throw std::runtime_error("index out of range"); real_index = real_index * info.elements + ( n - info.lBound ); elem_size /= info.elements; } return std::pair(real_index * elem_size, elem_size); } private: static size_type svtoi(std::string_view s) { size_type result = 0; for ( ; !s.empty(); ) { if ( s[0] < '0' || s[0] > '9' ) throw std::runtime_error("index corrupted"); result = result * 10 + ( s[0] - '0' ); s.remove_prefix(1); } return result; } const char* dim_data; size_type dims; size_type size; }; class adsDT { public: explicit adsDT(const char* raw_dt_data, bool check_size = true) : dt_data(raw_dt_data) { if ( check_size ) check_item_size(this_len(dt_data), dt_data, 1); if ( PADSDATATYPENAME(dt_data)[name_len(dt_data)] != '\0' || PADSDATATYPETYPE(dt_data)[type_len(dt_data)] != '\0' || PADSDATATYPECOMMENT(dt_data)[comment_len(dt_data)] != '\0' ) throw std::runtime_error("datatype corrupted: missing terminator"); if ( strlen(name().data()) != name().length() || strlen(type().data()) != type().length() || strlen(comment().data()) != comment().length() ) throw std::runtime_error("datatype corrupted: embedded \\0"); } adsDT(const adsDT&) noexcept = default; adsDT(adsDT&&) noexcept = default; adsDT& operator=(const adsDT&) noexcept = default; adsDT& operator=(adsDT&&) noexcept = default; ~adsDT() noexcept = default; std::string_view name() const noexcept { return { PADSDATATYPENAME(dt_data), name_len(dt_data) }; } std::string_view type() const noexcept { return { PADSDATATYPETYPE(dt_data), type_len(dt_data) }; } std::string_view comment() const noexcept { return { PADSDATATYPECOMMENT(dt_data), comment_len(dt_data) }; } auto offset() const noexcept { return read_raw<decltype(AdsDatatypeEntry::offs)>(dt_data + offsetof(AdsDatatypeEntry, offs)); } auto size() const noexcept { return read_raw<decltype(AdsDatatypeEntry::size)>(dt_data + offsetof(AdsDatatypeEntry, size)); } static const adsDT* find_by_name(const std::vector<adsDT>& data, const std::string_view& name) noexcept { auto it = std::lower_bound(data.begin(), data.end(), name, [](const adsDT& lhs, const std::string_view& rhs) { return lhs.name() < rhs; } ); return it != data.end() && it->name() == name ? &*it : nullptr; } static std::size_t this_len(const char* data) noexcept { return read_raw<decltype(AdsDatatypeEntry::entryLength)>(data + offsetof(AdsDatatypeEntry, entryLength)); } auto sub_symbols() const noexcept { return sub_items(dt_data); } const char* sub_symbol_data() const noexcept { return dt_data + sizeof(AdsDatatypeEntry) + 3 + name_len(dt_data) + type_len(dt_data) + comment_len(dt_data) + array_dim(dt_data) * sizeof(AdsDatatypeArrayInfo); } array_info arr_info() const noexcept { return { dt_data + sizeof(AdsDatatypeEntry) + 3 + name_len(dt_data) + type_len(dt_data) + comment_len(dt_data), array_dim(dt_data), size() }; } private: static std::size_t name_len(const char* data) noexcept { return read_raw<decltype(AdsDatatypeEntry::nameLength)>(data + offsetof(AdsDatatypeEntry, nameLength)); } static std::size_t type_len(const char* data) noexcept { return read_raw<decltype(AdsDatatypeEntry::typeLength)>(data + offsetof(AdsDatatypeEntry, typeLength)); } static std::size_t comment_len(const char* data) noexcept { return read_raw<decltype(AdsDatatypeEntry::commentLength)>(data + offsetof(AdsDatatypeEntry, commentLength)); } static std::size_t array_dim(const char* data) noexcept { return read_raw<decltype(AdsDatatypeEntry::arrayDim)>(data + offsetof(AdsDatatypeEntry, arrayDim)); } static std::size_t sub_items(const char* data) noexcept { return read_raw<decltype(AdsDatatypeEntry::subItems)>(data + offsetof(AdsDatatypeEntry, subItems)); } static void check_item_size(std::size_t limit, const char* data, std::size_t count) { for ( ; count--; ) { if ( this_len(data) < sizeof(AdsDatatypeEntry) + 3 ) throw std::runtime_error("datatype corrupted: entryLength (1)"); if ( this_len(data) > limit ) throw std::runtime_error("datatype corrupted: entryLength (2)"); if ( count && limit - this_len(data) < sizeof(AdsDatatypeEntry) + 3 ) throw std::runtime_error("datatype corrupted: entryLength (3)"); auto arrayinfo_offset = checked_sum({ sizeof(AdsDatatypeEntry), 3, name_len(data), type_len(data), comment_len(data) }); if ( this_len(data) < arrayinfo_offset ) throw std::runtime_error("datatype corrupted: entryLength (4)"); if ( ( this_len(data) - arrayinfo_offset ) / sizeof(AdsDatatypeArrayInfo) < array_dim(data) ) throw std::runtime_error("datatype corrupted: arrayDim"); auto subitems_offset = arrayinfo_offset + array_dim(data) * sizeof(AdsDatatypeArrayInfo); if ( this_len(data) < subitems_offset ) throw std::runtime_error("datatype corrupted: entryLength (5)"); if ( ( this_len(data) - subitems_offset ) / (sizeof(AdsDatatypeEntry) + 3) < sub_items(data) ) throw std::runtime_error("datatype corrupted: subItems"); check_item_size(this_len(data) - subitems_offset, data + subitems_offset, sub_items(data)); limit -= this_len(data); data += this_len(data); } if ( limit != 0 ) throw std::runtime_error("datatype corrupted: unused memory"); } const char* dt_data; };
std::vector<adsDT> parse_dt_data(const std::vector<char>& dt_data) { std::vector<adsDT> result; auto len = dt_data.size(); if ( len == 0 ) return result; std::size_t dt_count = 0; for ( auto p = &dt_data[0]; len != 0; ) { if ( len < sizeof(AdsDatatypeEntry) + 3) throw std::runtime_error("datatypes corrupted (1)"); auto cur_len = adsDT::this_len(p); if ( cur_len < sizeof(AdsDatatypeEntry) + 3 || cur_len > len ) throw std::runtime_error("datatypes corrupted (2)"); ++dt_count; p += cur_len; len -= cur_len; } result.reserve(dt_count); for ( auto p = &dt_data[0]; dt_count--; ) { result.emplace_back(p); p += adsDT::this_len(p); } // should be sorted & unique if ( std::adjacent_find(result.begin(), result.end(), [](const adsDT& lhs, const adsDT& rhs) { return !(lhs.name() < rhs.name()); } ) != result.end() ) throw std::runtime_error("datatypes not sorted or not unique"); return result; } class adsSym { public: explicit adsSym(const char* raw_sym_data) : sym_data(raw_sym_data) { check_item_size(sym_data); if ( PADSSYMBOLNAME(sym_data)[name_len(sym_data)] != '\0' || PADSSYMBOLTYPE(sym_data)[type_len(sym_data)] != '\0' || PADSSYMBOLCOMMENT(sym_data)[comment_len(sym_data)] != '\0' ) throw std::runtime_error("Symbol corrupted: missing terminator"); if ( strlen(name().data()) != name().length() || strlen(type().data()) != type().length() || strlen(comment().data()) != comment().length() ) throw std::runtime_error("Symbol corrupted: embedded \\0"); if ( name().find_first_of(".["sv) != name().npos ) throw std::runtime_error("Symbol name corrupted: embedded . or ["); } adsSym(const adsSym&) noexcept = default; adsSym(adsSym&&) noexcept = default; adsSym& operator=(const adsSym&) noexcept = default; adsSym& operator=(adsSym&&) noexcept = default; ~adsSym() noexcept = default; std::string_view name() const noexcept { return { PADSSYMBOLNAME(sym_data), name_len(sym_data) }; } std::string_view type() const noexcept { return { PADSSYMBOLTYPE(sym_data), type_len(sym_data) }; } std::string_view comment() const noexcept { return { PADSSYMBOLCOMMENT(sym_data), comment_len(sym_data) }; } auto size() const noexcept { return read_raw<decltype(AdsSymbolEntry::size)>(sym_data + offsetof(AdsSymbolEntry, size)); } auto indexGroup() const noexcept { return read_raw<decltype(AdsSymbolEntry::iGroup)>(sym_data + offsetof(AdsSymbolEntry, iGroup)); } auto indexOffset() const noexcept { return read_raw<decltype(AdsSymbolEntry::iOffs)>(sym_data + offsetof(AdsSymbolEntry, iOffs)); } static const adsSym* find_by_name(const std::vector<adsSym>& data, const std::string_view& name) noexcept { auto it = std::lower_bound(data.begin(), data.end(), name, [](const adsSym& lhs, const std::string_view& rhs) { return lhs.name() < rhs; } ); return it != data.end() && it->name() == name ? &*it : nullptr; } static std::size_t this_len(const char* data) noexcept { return read_raw<decltype(AdsSymbolEntry::entryLength)>(data + offsetof(AdsSymbolEntry, entryLength)); } private: static std::size_t name_len(const char* data) noexcept { return read_raw<decltype(AdsSymbolEntry::nameLength)>(data + offsetof(AdsSymbolEntry, nameLength)); } static std::size_t type_len(const char* data) noexcept { return read_raw<decltype(AdsSymbolEntry::typeLength)>(data + offsetof(AdsSymbolEntry, typeLength)); } static std::size_t comment_len(const char* data) noexcept { return read_raw<decltype(AdsSymbolEntry::commentLength)>(data + offsetof(AdsSymbolEntry, commentLength)); } static void check_item_size(const char* data) { if ( this_len(data) < sizeof(AdsSymbolEntry) + 3 ) throw std::runtime_error("Symbol corrupted (1)"); if ( this_len(data) != checked_sum({ sizeof(AdsSymbolEntry), 3, name_len(data), type_len(data), comment_len(data) }) ) throw std::runtime_error("Symbol corrupted (2)"); } const char* sym_data; }; std::vector<adsSym> parse_sym_data(const std::vector<char>& sym_data) { std::vector<adsSym> result; auto len = sym_data.size(); if ( len == 0 ) return result; std::size_t sym_count = 0; for ( auto p = &sym_data[0]; len != 0; ) { if ( len < sizeof(AdsSymbolEntry) + 3) throw std::runtime_error("symboldata corrupted (1)"); auto cur_len = adsSym::this_len(p); if ( cur_len < sizeof(AdsSymbolEntry) + 3 || cur_len > len ) throw std::runtime_error("symboldata corrupted (2)"); ++sym_count; p += cur_len; len -= cur_len; } result.reserve(sym_count); for ( auto p = &sym_data[0]; sym_count--; ) { result.emplace_back(p); p += adsSym::this_len(p); } // should be sorted & unique if ( std::adjacent_find(result.begin(), result.end(), [](const adsSym& lhs, const adsSym& rhs) { return !(lhs.name() < rhs.name()); } ) != result.end() ) throw std::runtime_error("symbols not sorted or not unique"); return result; } template <typename T, typename std::enable_if_t<std::is_same_v<T, adsSym> || std::is_same_v<T, adsDT>, int> = 0> void update_var(ITcAdsSymbol& var, const T& data, decltype(ITcAdsSymbol::indexGroup) group, decltype(ITcAdsSymbol::indexOffset) offset, decltype(AdsSymbolEntry::size) size) { var.shortName = data.name(); var.type = data.type(); var.comment = data.comment(); var.size = size; var.indexGroup = group; var.indexOffset = offset; //var.datatype = } void update_from_sps_data(std::vector<ITcAdsSymbol>& vars, const std::vector<char>& sym_data, const std::vector<char>& dt_data) { auto datatypes = parse_dt_data(dt_data); auto symbols = parse_sym_data(sym_data); for ( auto& var : vars ) { std::string_view cur_name = var.name; auto sym_name = cur_name; auto pos = sym_name.find_first_of(".["); if ( pos != sym_name.npos ) sym_name.remove_suffix(sym_name.size() - pos); auto sym = adsSym::find_by_name(symbols, sym_name); if ( !sym ) throw std::runtime_error("var not found"); auto group = sym->indexGroup(); auto offset = sym->indexOffset(); if ( pos == cur_name.npos ) { update_var(var, *sym, group, offset, sym->size()); continue; } auto type_name = sym->type(); for ( ;; ) { auto dt = adsDT::find_by_name(datatypes, type_name); if ( !dt ) throw std::runtime_error("type not found"); cur_name.remove_prefix(pos); if ( cur_name[0] == '.' ) { cur_name.remove_prefix(1); auto short_name = cur_name; pos = short_name.find_first_of(".["); if ( pos != short_name.npos ) { short_name.remove_suffix(short_name.size() - pos); } // check all subsymbols in order auto sub = dt->sub_symbol_data(); std::size_t n = 0; for ( ; n != dt->sub_symbols(); ++n ) { if ( adsDT(sub, false).name() == short_name ) break; } if ( n == dt->sub_symbols() ) throw std::runtime_error("subsymbol not found"); adsDT sub_item(sub, false); offset += sub_item.offset(); if ( pos == short_name.npos ) { update_var(var, sub_item, group, offset, sub_item.size()); break; } type_name = sub_item.type(); } else { // cur_name[0] == '[' cur_name.remove_prefix(1); pos = cur_name.find("]"); if ( pos == cur_name.npos ) throw std::runtime_error("missing ]"); auto index = cur_name; index.remove_suffix(index.size() - pos); auto [elem_offset, elem_size] = dt->arr_info().elem_info(index); offset += elem_offset; cur_name.remove_prefix(pos + 1); if ( cur_name.empty() ) { update_var(var, *dt, group, offset, elem_size); break; } if ( cur_name[0] != '[' && cur_name[0] != '.' ) throw std::runtime_error("missing . or ["); pos = 0; type_name = dt->type(); } } } } int main() { std::vector<ITcAdsSymbol> vars; std::vector<char> raw; try { update_from_sps_data(vars, raw, raw); } catch ( std::runtime_error& e ) { std::cout << e.what() << '\n'; return 1; } }
-
Ok. Wollte noch vor deiner letzten Antwort sagen: "Wie soll das gehen".
Da bin ich jetzt aber baff. Danke. Ich werde das sofort am Montag testen. Hatte heute Urlaub.
Vielen Dank erst mal dafür. Muss mir das erst mal in Ruhe ansehen.
-
Noch ein bisschen damit herumgespielt. Das scheint (zumindest) rudimentär zu funktionieren.
adsVarData benutzt hier für strings bis auf den vollen Variablennamen jeweils string_view, d.h. die von der SPS geladenen Daten müssen im Speicher bleiben. Dafür werden unnötige Heapallokationen für strings vermieden. WTFPL gilt.90% des Codes ist leider langweilig, ausserdem versaut Visual C++ die Formatierung
adsData.h#pragma once #include <cstddef> #include <cstdint> #include <string> #include <string_view> #include <vector> #include <wtypes.h> #include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsDef.h" #include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsAPI.h" using namespace std::literals::string_view_literals; enum class adsDatatypeId : std::uint16_t { void_ = VT_EMPTY, int8_ = VT_I1, uint8_ = VT_UI1, int16_ = VT_I2, uint16_ = VT_UI2, int32_ = VT_I4, uint32_ = VT_UI4, int64_ = VT_I8, uint64_ = VT_UI8, float_ = VT_R4, double_ = VT_R8, cstring_ = VT_LPSTR, wcstring_ = VT_LPWSTR, ldouble_ = VT_LPWSTR + 1, bool_ = 33, bit_ = VT_LPWSTR + 2, big_ = VT_BLOB, }; struct adsVarData { std::string name; std::string_view shortName; std::string_view typeName; std::string_view comment; decltype(AdsSymbolEntry::size) size; decltype(AdsSymbolEntry::iGroup) group; decltype(AdsSymbolEntry::iOffs) offset; adsDatatypeId type; }; #include "adsData_impl.h" class adsData { public: explicit adsData(); adsData(std::vector<char>&& sym_data, std::vector<char>&& dt_data); adsData(const adsData&) = delete; adsData& operator=(const adsData&) = delete; adsData(adsData&&) noexcept = default; adsData& operator=(adsData&&) noexcept = default; ~adsData() noexcept = default; adsVarData operator[](std::string name) const; private: std::vector<char> sym_data; std::vector<char> dt_data; std::vector<adsData_impl::adsSym> symbols; std::vector<adsData_impl::adsDT> datatypes; };
adsData_impl.h
#pragma once #include <algorithm> #include <cstring> #include <string_view> #include <stdexcept> namespace adsData_impl { template <typename T, std::enable_if_t<std::is_trivial_v<T>, int> = 0> T read_raw(const char* p) noexcept { T res; std::memcpy(&res, p, sizeof res); return res; } #define READ_RAW(p, class, member) read_raw<decltype(class::member)>(p + offsetof(class, member)) inline std::size_t checked_sum(std::initializer_list<std::size_t> args) { std::size_t result = 0; for (auto arg : args) { auto sum = result + arg; if (sum < result) throw std::overflow_error("data corrupted: length"); result = sum; } return result; } class array_info { using size_type = decltype(AdsSymbolEntry::iOffs); public: array_info(const char* dim_data, std::size_t dims, std::size_t size) noexcept : dim_data(dim_data), dims(dims), size(size) {} array_info(const array_info&) noexcept = default; array_info(array_info&&) noexcept = default; array_info& operator=(const array_info&) noexcept = default; array_info& operator=(array_info&&) noexcept = default; ~array_info() noexcept = default; auto elem_info(std::string_view index) const { size_type real_index = 0; auto elem_size = size; for (size_type i = 0; i != dims; ++i) { auto pos = index.find(','); if ((i != dims - 1) == (pos == index.npos)) throw std::out_of_range("index with wrong number of dimensions"); auto cur_index = index; if (pos != index.npos) { cur_index.remove_suffix(cur_index.size() - pos); index.remove_prefix(pos + 1); } auto n = svtoi(cur_index); auto info = read_raw<AdsDatatypeArrayInfo>(dim_data + i * sizeof(AdsDatatypeArrayInfo)); if (n < info.lBound || n - info.lBound >= info.elements) throw std::out_of_range("index out of range"); real_index = real_index * info.elements + (n - info.lBound); if (elem_size % info.elements != 0) throw std::runtime_error("array info corrupted"); elem_size /= info.elements; } return std::make_pair(real_index * elem_size, elem_size); } private: static size_type svtoi(std::string_view s) { size_type result = 0; if (s.empty()) throw std::out_of_range("index corrupted"); for (; !s.empty(); ) { if (s[0] < '0' || s[0] > '9') throw std::out_of_range("index corrupted"); result = result * 10 + (s[0] - '0'); s.remove_prefix(1); } return result; } const char* dim_data; size_type dims; size_type size; }; class adsDT { public: explicit adsDT(const char* raw_dt_data, bool check_size = true) : dt_data{ raw_dt_data } { if (check_size) check_item_size(this_len(dt_data), dt_data, 1); if (PADSDATATYPENAME(dt_data)[name_len(dt_data)] != '\0' || PADSDATATYPETYPE(dt_data)[type_len(dt_data)] != '\0' || PADSDATATYPECOMMENT(dt_data)[comment_len(dt_data)] != '\0') throw std::runtime_error("datatype corrupted: missing terminator"); if (strlen(name().data()) != name().length() || strlen(type_name().data()) != type_name().length() || strlen(comment().data()) != comment().length()) throw std::runtime_error("datatype corrupted: embedded \\0"); } adsDT(const adsDT&) noexcept = default; adsDT(adsDT&&) noexcept = default; adsDT& operator=(const adsDT&) noexcept = default; adsDT& operator=(adsDT&&) noexcept = default; ~adsDT() noexcept = default; std::string_view name() const noexcept { return { PADSDATATYPENAME(dt_data), name_len(dt_data) }; } std::string_view type_name() const noexcept { return { PADSDATATYPETYPE(dt_data), type_len(dt_data) }; } std::string_view comment() const noexcept { return { PADSDATATYPECOMMENT(dt_data), comment_len(dt_data) }; } auto offset() const noexcept { return READ_RAW(dt_data, AdsDatatypeEntry, offs); } auto size() const noexcept { return READ_RAW(dt_data, AdsDatatypeEntry, size); } auto type_id() const noexcept { return static_cast<adsDatatypeId>(READ_RAW(dt_data, AdsDatatypeEntry, dataType)); } array_info arr_info() const noexcept { return { dt_data + sizeof(AdsDatatypeEntry) + 3 + name_len(dt_data) + type_len(dt_data) + comment_len(dt_data), array_dim(dt_data), size() }; } auto sub_symbols() const noexcept { return sub_items(dt_data); } const char* sub_symbol_data() const noexcept { return dt_data + sizeof(AdsDatatypeEntry) + 3 + name_len(dt_data) + type_len(dt_data) + comment_len(dt_data) + array_dim(dt_data) * sizeof(AdsDatatypeArrayInfo); } static const adsDT* find_by_name(const std::vector<adsDT>& data, const std::string_view& name) noexcept { auto it = std::lower_bound(data.begin(), data.end(), name, [](const adsDT& lhs, const std::string_view& rhs) { return lhs.name() < rhs; }); return it != data.end() && it->name() == name ? &*it : nullptr; } static std::size_t this_len(const char* p) noexcept { return READ_RAW(p, AdsDatatypeEntry, entryLength); } private: static std::size_t name_len(const char* p) noexcept { return READ_RAW(p, AdsDatatypeEntry, nameLength); } static std::size_t type_len(const char* p) noexcept { return READ_RAW(p, AdsDatatypeEntry, typeLength); } static std::size_t comment_len(const char* p) noexcept { return READ_RAW(p, AdsDatatypeEntry, commentLength); } static std::size_t array_dim(const char* p) noexcept { return READ_RAW(p, AdsDatatypeEntry, arrayDim); } static std::size_t sub_items(const char* p) noexcept { return READ_RAW(p, AdsDatatypeEntry, subItems); } static void check_item_size(std::size_t limit, const char* data, std::size_t count) { for (; count--; ) { if (this_len(data) < sizeof(AdsDatatypeEntry) + 3) throw std::runtime_error("datatype corrupted: entryLength (1)"); if (this_len(data) > limit) throw std::runtime_error("datatype corrupted: entryLength (2)"); if (count && limit - this_len(data) < sizeof(AdsDatatypeEntry) + 3) throw std::runtime_error("datatype corrupted: entryLength (3)"); auto arrayinfo_offset = checked_sum({ sizeof(AdsDatatypeEntry) + 3, name_len(data), type_len(data), comment_len(data) }); if (this_len(data) < arrayinfo_offset) throw std::runtime_error("datatype corrupted: entryLength (4)"); if ((this_len(data) - arrayinfo_offset) / sizeof(AdsDatatypeArrayInfo) < array_dim(data)) throw std::runtime_error("datatype corrupted: arrayDim"); auto subitems_offset = arrayinfo_offset + array_dim(data) * sizeof(AdsDatatypeArrayInfo); if ((this_len(data) - subitems_offset) / (sizeof(AdsDatatypeEntry) + 3) < sub_items(data)) throw std::runtime_error("datatype corrupted: subItems"); check_item_size(this_len(data) - subitems_offset, data + subitems_offset, sub_items(data)); limit -= this_len(data); data += this_len(data); } if (limit != 0) throw std::runtime_error("datatype corrupted: unused memory"); } const char* dt_data; }; class adsSym { public: explicit adsSym(const char* raw_sym_data) : sym_data{ raw_sym_data } { check_item_size(sym_data); if (PADSSYMBOLNAME(sym_data)[name_len(sym_data)] != '\0' || PADSSYMBOLTYPE(sym_data)[type_len(sym_data)] != '\0' || PADSSYMBOLCOMMENT(sym_data)[comment_len(sym_data)] != '\0') throw std::runtime_error("Symbol corrupted: missing terminator"); if (strlen(name().data()) != name().length() || strlen(type_name().data()) != type_name().length() || strlen(comment().data()) != comment().length()) throw std::runtime_error("Symbol corrupted: embedded \\0"); if (name().find('[') != name().npos) throw std::runtime_error("Symbol name must not contain '['"); } adsSym(const adsSym&) noexcept = default; adsSym(adsSym&&) noexcept = default; adsSym& operator=(const adsSym&) noexcept = default; adsSym& operator=(adsSym&&) noexcept = default; ~adsSym() noexcept = default; std::string_view name() const noexcept { return { PADSSYMBOLNAME(sym_data), name_len(sym_data) }; } std::string_view type_name() const noexcept { return { PADSSYMBOLTYPE(sym_data), type_len(sym_data) }; } std::string_view comment() const noexcept { return { PADSSYMBOLCOMMENT(sym_data), comment_len(sym_data) }; } auto type_id() const noexcept { return static_cast<adsDatatypeId>(READ_RAW(sym_data, AdsSymbolEntry, dataType)); } auto size() const noexcept { return READ_RAW(sym_data, AdsSymbolEntry, size); } auto group() const noexcept { return READ_RAW(sym_data, AdsSymbolEntry, iGroup); } auto offset() const noexcept { return READ_RAW(sym_data, AdsSymbolEntry, iOffs); } static const adsSym* find_by_name(const std::vector<adsSym>& data, const std::string_view& name) noexcept { // TODO: find something less ugly static_assert('.' < 'A' && '.' < '0'); auto it = std::upper_bound(data.begin(), data.end(), name, [](const std::string_view& lhs, const adsSym& rhs) noexcept { return lhs < rhs.name(); }); return it != data.begin() && (--it)->name().size() <= name.size() && it->name() == std::string_view{ name.data(), it->name().size() } ? &*it : nullptr; } static std::size_t this_len(const char* p) noexcept { return READ_RAW(p, AdsSymbolEntry, entryLength); } private: static std::size_t name_len(const char* p) noexcept { return READ_RAW(p, AdsSymbolEntry, nameLength); } static std::size_t type_len(const char* p) noexcept { return READ_RAW(p, AdsSymbolEntry, typeLength); } static std::size_t comment_len(const char* p) noexcept { return READ_RAW(p, AdsSymbolEntry, commentLength); } static void check_item_size(const char* data) { if (this_len(data) < sizeof(AdsSymbolEntry) + 3) throw std::runtime_error("Symbol corrupted (1)"); if (this_len(data) != checked_sum({ sizeof(AdsSymbolEntry) + 3, name_len(data), type_len(data), comment_len(data) })) throw std::runtime_error("Symbol corrupted (2)"); } const char* sym_data; }; }
adsData.cpp
#include <iostream> #include <memory> #include <stdexcept> #include <string> #include <string_view> #include <type_traits> #include <vector> #include "adsData.h" namespace { std::vector<adsData_impl::adsDT> parse_dt_data(const std::vector<char>& dt_data) { std::vector<adsData_impl::adsDT> result; auto len = dt_data.size(); if (len == 0) return result; std::size_t dt_count = 0; for (auto p = &dt_data[0]; len != 0; ) { if (len < sizeof(AdsDatatypeEntry) + 3) throw std::runtime_error("datatypes corrupted (1)"); auto cur_len = adsData_impl::adsDT::this_len(p); if (cur_len < sizeof(AdsDatatypeEntry) + 3 || cur_len > len) throw std::runtime_error("datatypes corrupted (2)"); ++dt_count; p += cur_len; len -= cur_len; } result.reserve(dt_count); for (auto p = &dt_data[0]; dt_count--; ) { result.emplace_back(p); p += adsData_impl::adsDT::this_len(p); } std::sort(result.begin(), result.end(), [](auto& lhs, auto& rhs) noexcept { return lhs.name() < rhs.name(); }); if (std::adjacent_find(result.begin(), result.end(), [](auto& lhs, auto& rhs) noexcept { return !(lhs.name() < rhs.name()); }) != result.end()) throw std::runtime_error("datatypes not unique"); return result; } std::vector<adsData_impl::adsSym> parse_sym_data(const std::vector<char>& sym_data) { std::vector<adsData_impl::adsSym> result; auto len = sym_data.size(); if (len == 0) return result; std::size_t sym_count = 0; for (auto p = &sym_data[0]; len != 0; ) { if (len < sizeof(AdsSymbolEntry) + 3) throw std::runtime_error("symboldata corrupted (1)"); auto cur_len = adsData_impl::adsSym::this_len(p); if (cur_len < sizeof(AdsSymbolEntry) + 3 || cur_len > len) throw std::runtime_error("symboldata corrupted (2)"); ++sym_count; p += cur_len; len -= cur_len; } result.reserve(sym_count); for (auto p = &sym_data[0]; sym_count--; ) { result.emplace_back(p); p += adsData_impl::adsSym::this_len(p); } std::sort(result.begin(), result.end(), [](auto& lhs, auto& rhs) noexcept { return lhs.name() < rhs.name(); }); if (std::adjacent_find(result.begin(), result.end(), [](auto& lhs, auto& rhs) noexcept { return !(lhs.name() < rhs.name()); }) != result.end()) throw std::runtime_error("symbols not unique"); return result; } template <typename T, typename std::enable_if_t<std::is_same_v<T, adsData_impl::adsDT> || std::is_same_v<T, adsData_impl::adsSym>, int> = 0> adsVarData get_var(std::string&& name, const T& data, decltype(adsVarData::group) group, decltype(adsVarData::offset) offset, decltype(adsVarData::size) size) noexcept { return { std::move(name), data.name(), data.type_name(), data.comment(), size, group, offset, data.type_id() }; } std::string to_string_hex(std::uint64_t val) { char buf[2*sizeof(val) + 1]; std::snprintf(buf, sizeof(buf), "%llx", val); return buf; } } // namespace adsData::adsData() : sym_data{}, dt_data{}, symbols{}, datatypes{} { auto ads_port = AdsPortOpenEx(); if (ads_port == 0) throw std::runtime_error("cannot open ads port"); { std::unique_ptr<decltype(ads_port), void(*)(decltype(ads_port)*)> guard(&ads_port, [](decltype(ads_port)* p) { AdsPortCloseEx(*p); }); AmsAddr ams_addr{}; if (auto err = AdsGetLocalAddressEx(ads_port, &ams_addr); err != ADSERR_NOERR) throw std::runtime_error("cannot get local ams address: 0x" + to_string_hex(err)); ams_addr.port = AMSPORT_R0_PLC_RTS1; AdsSymbolUploadInfo2 info{}; unsigned long bytes_read = 0; if (auto err = AdsSyncReadReqEx2(ads_port, &ams_addr, ADSIGRP_SYM_UPLOADINFO2, 0, sizeof(info), &info, &bytes_read); err != ADSERR_NOERR) throw std::runtime_error("cannot read symbol upload info: 0x" + to_string_hex(err)); if (bytes_read != sizeof(info)) throw std::runtime_error("error reading sym_uploadinfo2: " + std::to_string(bytes_read) + " bytes read"); sym_data.resize(info.nSymSize); dt_data.resize(info.nDatatypeSize); if (auto err = AdsSyncReadReqEx2(ads_port, &ams_addr, ADSIGRP_SYM_UPLOAD, 0, info.nSymSize, &sym_data[0], &bytes_read); err != ADSERR_NOERR) throw std::runtime_error("cannot read symbol info: 0x" + to_string_hex(err)); if (bytes_read != info.nSymSize) throw std::runtime_error("error reading symbols: " + std::to_string(bytes_read) + " bytes read, " + std::to_string(info.nSymSize) + " expected"); if (auto err = AdsSyncReadReqEx2(ads_port, &ams_addr, ADSIGRP_SYM_DT_UPLOAD, 0, info.nDatatypeSize, &dt_data[0], &bytes_read); err != ADSERR_NOERR) throw std::runtime_error("cannot read datatype info: 0x" + to_string_hex(err)); if (bytes_read != info.nDatatypeSize) throw std::runtime_error("error reading datatypes: " + std::to_string(bytes_read) + " bytes read, " + std::to_string(info.nDatatypeSize) + " expected"); } symbols = parse_sym_data(sym_data); datatypes = parse_dt_data(dt_data); } adsData::adsData(std::vector<char>&& sym_data_, std::vector<char>&& dt_data_) : sym_data{ std::move(sym_data_) }, dt_data{ std::move(dt_data_) }, symbols{ parse_sym_data(sym_data) }, datatypes{ parse_dt_data(dt_data) } {} adsVarData adsData::operator[](std::string name) const { std::string_view cur_name = name; if (auto pos = cur_name.find('['); pos != cur_name.npos) cur_name.remove_suffix(cur_name.size() - pos); auto sym = adsData_impl::adsSym::find_by_name(symbols, cur_name); if (!sym) throw std::out_of_range("var not found: " + name); auto group = sym->group(); auto offset = sym->offset(); if (name.size() == sym->name().size()) return get_var(std::move(name), *sym, group, offset, sym->size()); cur_name = std::string_view{ name.data() + sym->name().size(), name.size() - sym->name().size() }; if (cur_name[0] != '.' && cur_name[0] != '[' ) throw std::out_of_range("var not found: " + name); std::size_t pos = 0; auto type_name = sym->type_name(); for (;;) { auto dt = adsData_impl::adsDT::find_by_name(datatypes, type_name); if (!dt) throw std::out_of_range("type not found"); cur_name.remove_prefix(pos); if (cur_name[0] == '.') { cur_name.remove_prefix(1); auto short_name = cur_name; pos = short_name.find_first_of(".["sv); if (pos != short_name.npos) { short_name.remove_suffix(short_name.size() - pos); } // check all subsymbols in order auto sub = dt->sub_symbol_data(); std::size_t n = 0; for (; n != dt->sub_symbols(); ++n) { if (adsData_impl::adsDT(sub, false).name() == short_name) break; sub += adsData_impl::adsDT::this_len(sub); } if (n == dt->sub_symbols()) throw std::out_of_range("subsymbol not found"); adsData_impl::adsDT sub_item(sub, false); offset += sub_item.offset(); if (pos == short_name.npos) return get_var(std::move(name), sub_item, group, offset, sub_item.size()); type_name = sub_item.type_name(); } else { // cur_name[0] == '[' cur_name.remove_prefix(1); pos = cur_name.find(']'); if (pos == cur_name.npos) throw std::out_of_range("missing ]"); auto index = cur_name; index.remove_suffix(index.size() - pos); auto[elem_offset, elem_size] = dt->arr_info().elem_info(index); offset += elem_offset; cur_name.remove_prefix(pos + 1); if (cur_name.empty()) return get_var(std::move(name), *dt, group, offset, elem_size); if (cur_name[0] != '[' && cur_name[0] != '.') throw std::out_of_range("missing . or ["); pos = 0; type_name = dt->type_name(); } } }
main.cpp
#include <algorithm> #include <iostream> #include <stdexcept> #include <string> #include <utility> #include "adsData.h" std::ostream& operator<<(std::ostream& s, adsDatatypeId val) { static constexpr std::pair<adsDatatypeId, std::string_view> typenames[] = { { adsDatatypeId::void_, "void"sv, }, { adsDatatypeId::int16_, "int16"sv }, { adsDatatypeId::int32_, "int32"sv }, { adsDatatypeId::float_, "float"sv }, { adsDatatypeId::double_, "double"sv }, { adsDatatypeId::int8_, "int8"sv }, { adsDatatypeId::uint8_, "uint8"sv }, { adsDatatypeId::uint16_, "uint16"sv }, { adsDatatypeId::uint32_, "uint32"sv }, { adsDatatypeId::int64_, "int64"sv }, { adsDatatypeId::uint64_, "uint64"sv }, { adsDatatypeId::cstring_, "cstring"sv }, { adsDatatypeId::wcstring_, "wcstring"sv }, { adsDatatypeId::bool_, "bool"sv }, { adsDatatypeId::big_, "blob"sv }, }; auto it = std::lower_bound(std::begin(typenames), std::end(typenames), val, [](auto& x, auto& y) { return x.first < y; }); if (it == std::end(typenames) || it->first != val) s << "unknown(" << static_cast<int>(val) << ')'; else s << it->second << '(' << static_cast<int>(val) << ')'; return s; } std::ostream& operator << (std::ostream& s, const adsVarData& var) { s << "name: " << var.name << '\n' << "short: " << var.shortName << '\n' << "typename: " << var.typeName << '\n' << "comment: " << var.comment << '\n' << "type: " << var.type << '\n' << "group: " << var.group << '\n' << "offset: " << var.offset << '\n' << "size: " << var.size << '\n'; return s; } int main() { std::string var_names[] = { "MAIN.X", "MAIN.X.X", "MAIN.X.Y", "MAIN.X.X.INT8", "MAIN.X.X.INT16", "MAIN.X.X.INT32", "MAIN.X.X.UINT8", "MAIN.X.X.UINT16", "MAIN.X.X.UINT32", "MAIN.X.X.CSTRING", "MAIN.X.X.BOOL_", "MAIN.X.X.FLOAT", "MAIN.X.X.DOUBLE", "MAIN.X.Y", "MAIN.X.Y[0]", "MAIN.X.Y[15]", "MAIN.X.Y[15].BOOL_", "MAIN.X.Z", "MAIN.X.Z[4,150]", "MAIN.X.Z[4,150].INT8", "MAIN.A", "MAIN.AA", "MAIN.A[5]" }; try { adsData plc_data{}; for (auto& name : var_names) try { std::cout << plc_data[std::move(name)] << '\n'; } catch (std::out_of_range& e) { std::cout << e.what() << '\n'; } } catch (std::runtime_error& e) { std::cout << e.what() << '\n'; return 1; } }
-
Worum geht's hier noch mal?
-
Vielen Dank für deine Hilfe.
Muss noch einiges lernen :-):- string_view kannte ich noch nicht
- namespace ohne name kannte ich auch noch nicht.Und dann noch ne Frage:
Verstehe noch nicht ganz die Trennung von .h .cpp und _impl.h:
Bei mir war header immer definition und cpp implementierung. Was mach man in _impl.h genau? Und dann auch gleich nen eigenen namespace namespace adsData_impl. Kannte ich so noch nicht.
-
boster schrieb:
Verstehe noch nicht ganz die Trennung von .h .cpp und _impl.h:
Bei mir war header immer definition und cpp implementierung. Was mach man in _impl.h genau? Und dann auch gleich nen eigenen namespace namespace adsData_impl. Kannte ich so noch nicht.Das Ganze dient dazu, den Header, den der Anwender direkt inkludiert (und wo du im Zweifel als erstes nachschaust, was du überhaupt mit dem ganzen Zeug machen kannst) nicht mit Implementierungsdetails überfrachtet wird.
adsData enthält aber private Daten (vector<adsData> etc.), bei denen der Compiler zumindest die entsprechende Typinformation schon gesehen haben muss (dafür ist der _impl.h Header da, manchmal gibt man solchen Dateien auch andere Endungen, etwa .impl - wenn mit Bibliotheken gearbeitet wird, die sehr viel Templatecode enthalten, sieht man das öfter).
Weil es aber Implementationsdetails sind, gibt es keinen Grund, dafür den globalen Namensraum zu verwenden.
Eine mögliche Alternative wäre das pimpl-Idiom.Btw. adsSym::find_by_name ist mit ziemlicher Sicherheit fehlerhaft.