TwinCat Load Symbol Data @camper


  • Mod

    adsData.cpp Teil 1

    #include <algorithm>
    #include <cassert>
    #include <cctype>
    #include <cstring>
    #include <optional>
    #include <string>
    #include <string_view>
    #include <stdexcept>
    #include <type_traits>
    #include <vector>
    
    #include "adsData.h"
    
    using namespace std::literals::string_literals;
    using namespace std::literals::string_view_literals;
    
    adsVarData::adsVarData(const adsData::subSymbolInfo* info, sizeType group, sizeType offset, sizeType index = -1)
        : info_{ info }, group_{ group }, offset_{ offset }, index_{ index }
    {}
    
    namespace {
        std::string operator+(std::string lhs, std::string_view rhs) {
            return std::move(lhs.append(rhs.data(), rhs.size()));
        }
        std::string operator+(std::string_view lhs, const std::string& rhs) {
            std::string result;
            result.reserve(lhs.size() + rhs.size());
            result.assign(lhs.data(), lhs.size()).append(rhs);
            return result;
        }
        using sizeType = adsData::sizeType;
    
        inline bool icmp_less(std::string_view lhs, std::string_view rhs) {
            return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(),
                [](auto x, auto y) { return
                x == '\0' ? y != '\0' : y == '\0' ? false
                : x == '.' ? y != '.' : y == '.' ? false
                : std::tolower(x) < std::tolower(y); });
        }
        inline bool icmp_equal(std::string_view lhs, std::string_view rhs) {
            return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(),
                [](auto x, auto y) { return std::tolower(x) == std::tolower(y); });
        }
    
        template <typename T, std::enable_if_t<std::is_trivial_v<T>, int> = 0>
        T readRaw(const char* p) noexcept {
            T res;
            std::memcpy(&res, p, sizeof res);
            return res;
        }
    
        namespace detail {
    #define ASSERT_THROW_(c) \
    { \
        assert(c); \
        if (!(c)) \
            throw std::runtime_error(#c); \
    }
    #define ASSERT_THROW(c) ASSERT_THROW_(c)
    
            template <typename T> struct properties_of;
    
            enum class ref_prop {
                null,
                typeSize,
                entryLen,
                nameLen,
                typeLen,
                commentLen,
                nameData,
                typeData,
                commentData,
                name,
                type,
                comment,
                nameTerminator,
                typeTerminator,
                commentTerminator,
                group,
                offset,
                size,
                flags,
                hasGuid,
                guid,
                hasAttributes,
                numAttributes,
                attributeData,
                typeId,
                elements,
                lBound,
                valueLen,
                valueData,
                value,
                valueTerminator,
                reserved,
                expEntryLen,
                version,
                arrayDim,
                arrayInfo,
                subItems,
                subItemData,
                hasCopyMask,
                copyMaskLen,
                copyMask,
                hasMethods,
                methods,
                methodData,
                hasEnumInfos,
                enumInfos,
                enums,
                alignSize,
                lengthIsPara,
                vTableIndex,
                paras,
                paraData,
                isStatic
            };
    
            template <ref_prop Name, typename Accessor>
            struct ref_property {
                static constexpr ref_prop name = Name;
                using accessor = Accessor;
            };
    
            // simple property accessors
            template <typename T>
            struct fixed_size {
                static constexpr std::size_t get(const char*, const char*) { return sizeof(T); }
                static void check(const char*, const char*, std::size_t) noexcept {}
            };
            template <typename T>
            struct is_fixed_size : std::false_type {};
            template <typename T>
            struct is_fixed_size<fixed_size<T>> : std::true_type {};
            template <typename T>
            constexpr bool is_fixed_size_v = is_fixed_size<T>::value;
    
            template <typename T, std::size_t disp, typename U = T>
            struct element {
                static U get(const char* p, const char*) noexcept { return static_cast<U>(readRaw<T>(p + disp)); };
                static constexpr std::size_t offset(const char*, const char*) noexcept { return disp; }
                static constexpr std::size_t size(const char*, const char*) noexcept { return sizeof(T); }
                static constexpr std::size_t next_offset(const char* p, const char* q) noexcept { return offset(p, q) + size(p, q); }
                static void check(const char* p, const char* q, std::size_t entryLimit) {
                    ASSERT_THROW(offset(p, q) < entryLimit && entryLimit - offset(p, q) >= size(p, q));
                }
            };
            template <typename T, std::size_t disp, T expected>
            struct element_ : element<T, disp> {
                static void check(const char* p, const char* q, std::size_t entryLimit) {
                    element<T, disp>::check(p, q, entryLimit);
                    ASSERT_THROW(get(p, q) == expected);
                }
            };
    #define ELEMENT(CLASS, MEMBER) \
        element<std::decay_t<decltype(std::declval<CLASS&>().*&CLASS::MEMBER)>, offsetof(CLASS, MEMBER)>
    #define ELEMENT_TYPE(CLASS, MEMBER, TYPE) \
        element<std::decay_t<decltype(std::declval<CLASS&>().*&CLASS::MEMBER)>, offsetof(CLASS, MEMBER), TYPE>
    #define ELEMENT_EXP(CLASS, MEMBER, EXPECTED) \
        element_<std::decay_t<decltype(std::declval<CLASS&>().*&CLASS::MEMBER)>, offsetof(CLASS, MEMBER), EXPECTED>
    
            // compound property accessors
            template <typename T, ref_prop before, ref_prop expected = ref_prop::null>
            struct next_data {
                static decltype(auto) get(const char* p, const char* q) noexcept { return properties_of<T>::template next_offset<before>(p, q); };
                static void check(const char* p, const char* q, std::size_t) {
                    static_cast<void>(p);
                    static_cast<void>(q);
                    if constexpr (expected != ref_prop::null)
                        ASSERT_THROW(get(p, q) == properties_of<T>::template get<expected>(p, q));
                }
            };
            template <typename T, ref_prop before, typename U>
            struct next_element {
                static U get(const char* p, const char* q) noexcept { return readRaw<U>(p + offset(p, q)); };
                static constexpr std::size_t offset(const char* p, const char* q) noexcept { return properties_of<T>::template next_offset<before>(p, q); }
                static constexpr std::size_t size(const char*, const char*) noexcept { return sizeof(U); }
                static constexpr std::size_t next_offset(const char* p, const char* q) noexcept { return offset(p, q) + size(p, q); }
                static void check(const char* p, const char* q, std::size_t entryLimit) {
                    ASSERT_THROW(offset(p, q) < entryLimit && entryLimit - offset(p, q) >= size(p, q));
                }
            };
            template <typename T, ref_prop before, typename U, U expected>
            struct next_element_ : next_element<T, before, U> {
                static void check(const char* p, const char* q, std::size_t entryLimit) {
                    next_element<T, before, U>::check(p, q, entryLimit);
                    ASSERT_THROW(get(p, q) == expected);
                }
            };
    
            template <typename T, ref_prop data, ref_prop length>
            struct string {
                static constexpr std::string_view get(const char* p, const char* q) noexcept { return { p + offset(p, q), size(p, q) }; };
                static constexpr std::size_t offset(const char* p, const char* q) noexcept { return properties_of<T>::template get<data>(p, q); }
                static constexpr std::size_t size(const char* p, const char* q) noexcept { return properties_of<T>::template get<length>(p, q); }
                static constexpr std::size_t next_offset(const char* p, const char* q) noexcept { return offset(p, q) + size(p, q); }
                static void check(const char* p, const char* q, std::size_t entryLimit) {
                    ASSERT_THROW(offset(p, q) < entryLimit && entryLimit - offset(p, q) >= size(p, q));
                    ASSERT_THROW(get(p, q).find('\0') == std::string_view::npos); // no embedded \0
                }
            };
    
            template <typename T, ref_prop ref, unsigned long long  mask>
            struct flag {
                static constexpr bool get(const char* p, const char* q) noexcept { return (properties_of<T>::template get<ref>(p, q) & mask) != 0; };
                static void check(const char*, const char*, std::size_t) noexcept {}
            };
    
            template <typename T, ref_prop flag, typename Access>
            struct optional {
                static constexpr auto get(const char* p, const char* q) noexcept {
                    using return_type = std::optional<decltype(value(p, q))>;
                    return exists(p, q) ? return_type{ value(p, q) } : return_type{};
                }
                static constexpr auto value(const char* p, const char* q) noexcept { return Access::get(p, q); }
                static constexpr bool exists(const char* p, const char* q) noexcept { return properties_of<T>::template get<flag>(p, q); };
                static constexpr std::size_t offset(const char* p, const char* q) noexcept { return Access::offset(p, q); }
                static constexpr std::size_t size(const char* p, const char* q) noexcept { return exists(p, q) ? Access::size(p, q) : 0; }
                static constexpr std::size_t next_offset(const char* p, const char* q) noexcept { return offset(p, q) + size(p, q); }
                static void check(const char* p, const char* q, std::size_t entryLimit) {
                    ASSERT_THROW(exists(p, q) ? offset(p, q) < entryLimit : offset(p, q) <= entryLimit);
                    if (exists(p, q))
                        Access::check(p, q, entryLimit);
                }
            };
            template <typename T, ref_prop flag, typename Access, decltype(Access::get(std::declval<const char*>(), std::declval<const char*>())) default_>
            struct optional_ : optional<T, flag, Access> {
                static constexpr auto get(const char* p, const char* q) noexcept {
                    return exists(p, q) ? value(p, q) : default_;
                }
            };
    
            template <typename T, ref_prop before, ref_prop num, typename Element>
            struct array {
                static constexpr const char* get(const char* p, const char* q) noexcept { return p + offset(p, q); }
                static constexpr std::size_t numElements(const char* p, const char* q) noexcept { return properties_of<T>::template get<num>(p, q); }
                static constexpr std::size_t offset(const char* p, const char* q) noexcept { return properties_of<T>::template next_offset<before>(p, q); }
                static constexpr std::size_t size(const char* p, const char* q) noexcept {
                    if constexpr (is_fixed_size_v<typename properties_of<Element>::template accessor<ref_prop::entryLen>>) {
                        return numElements(p, q) * properties_of<Element>::template get<ref_prop::entryLen>(get(p, q), p);
                    } else {
                        auto first = get(p, q);
                        auto last = first;
                        for (auto n = numElements(p, q); n--; )
                            last += properties_of<Element>::template get<ref_prop::entryLen>(last, p);
                        return static_cast<std::size_t>(last - first);
                    }
                }
                static constexpr std::size_t next_offset(const char* p, const char* q) noexcept { return offset(p, q) + size(p, q); }
                static void check(const char* p, const char* q, std::size_t entryLimit) {
                    ASSERT_THROW(offset(p, q) <= entryLimit);
                    q = p;
                    entryLimit -= offset(p, q);
                    p += offset(p, q);
                    for (auto n = numElements(q, q); n--; ) {
                        ASSERT_THROW(sizeof(Element) <= entryLimit);
                        properties_of<Element>::check(p, q, entryLimit);
                        entryLimit -= properties_of<Element>::template get<ref_prop::entryLen>(p, q);
                        p += properties_of<Element>::template get<ref_prop::entryLen>(p, q);
                    }
                }
            };
    
            template <typename T, ref_prop before, typename U, ref_prop size_>
            struct var_int {
                static auto get(const char* p, const char* q) noexcept; // TODO
                static std::size_t offset(const char* p, const char* q) noexcept { return properties_of<T>::template next_offset<before>(p, q); }
                static std::size_t size(const char*, const char* q) noexcept { return properties_of<U>::template get<size_>(q, q); }
                static std::size_t next_offset(const char* p, const char* q) noexcept { return offset(p, q) + size(p, q); }
                static void check(const char* p, const char* q, std::size_t entryLimit) {
                    ASSERT_THROW(offset(p, q) < entryLimit && entryLimit - offset(p, q) >= size(p, q));
                }
            };
    
            /////////////////////////////////////////
            struct unmatchedAccessor {
                friend constexpr auto operator^(unmatchedAccessor, unmatchedAccessor) noexcept { return unmatchedAccessor{}; }
                template <typename T>
                friend constexpr auto operator^(unmatchedAccessor, T) noexcept { return T{}; }
                template <typename T>
                friend constexpr auto operator^(T, unmatchedAccessor) noexcept { return T{}; }
            };
    
            template <typename... Properties>
            struct structured_type {
                using type = structured_type<Properties...>;
    
                template <ref_prop name>
                static auto get_accessor() noexcept {
                    return (... ^ std::conditional_t<Properties::name == name, typename Properties::accessor, unmatchedAccessor>{});
                };
                template <ref_prop name>
                using accessor = decltype(get_accessor<name>());
    
                template <ref_prop name> static constexpr decltype(auto) get(const char* p, const char* q)         noexcept { return accessor<name>::get(p, q); }
                template <ref_prop name> static constexpr decltype(auto) offset(const char* p, const char* q)      noexcept { return accessor<name>::offset(p, q); }
                template <ref_prop name> static constexpr decltype(auto) size(const char* p, const char* q)        noexcept { return accessor<name>::size(p, q); }
                template <ref_prop name> static constexpr decltype(auto) next_offset(const char* p, const char* q) noexcept { return accessor<name>::next_offset(p, q); }
                template <ref_prop name> static constexpr decltype(auto) value(const char* p, const char* q)       noexcept { return accessor<name>::value(p, q); }
                template <ref_prop name> static constexpr decltype(auto) exists(const char* p, const char* q)      noexcept { return accessor<name>::exists(p, q); }
                template <ref_prop name> static constexpr decltype(auto) numElements(const char* p, const char* q) noexcept { return accessor<name>::numElements(p, q); }
    
                static void check(const char* p, const char* q, std::size_t entryLimit) {
                    (..., Properties::accessor::check(p, q, entryLimit)); // check all properties in order
                }
            };
    
            template <>
            struct properties_of<ADS_UINT8>
                : structured_type<
                ref_property<ref_prop::entryLen, fixed_size<  ADS_UINT8>>>
            {};
    
            template <>
            struct properties_of<AdsDatatypeArrayInfo>
                : structured_type<
                ref_property<ref_prop::entryLen, fixed_size<  AdsDatatypeArrayInfo>>,
                ref_property<ref_prop::lBound, ELEMENT(AdsDatatypeArrayInfo, lBound)>,
                ref_property<ref_prop::elements, ELEMENT(AdsDatatypeArrayInfo, elements)>>
            {};
    
            template <>
            struct properties_of<AdsSymbolEntry>
                : structured_type<
                ref_property<ref_prop::entryLen, ELEMENT(AdsSymbolEntry, entryLength)>,
                ref_property<ref_prop::group, ELEMENT(AdsSymbolEntry, iGroup)>,
                ref_property<ref_prop::offset, ELEMENT(AdsSymbolEntry, iOffs)>,
                ref_property<ref_prop::size, ELEMENT(AdsSymbolEntry, size)>,
                ref_property<ref_prop::typeId, ELEMENT_TYPE(AdsSymbolEntry, dataType, adsDatatypeId)>,
                ref_property<ref_prop::flags, ELEMENT(AdsSymbolEntry, flags)>,
                ref_property<ref_prop::reserved, ELEMENT_EXP(AdsSymbolEntry, reserved, 0)>,
                ref_property<ref_prop::nameLen, ELEMENT(AdsSymbolEntry, nameLength)>,
                ref_property<ref_prop::typeLen, ELEMENT(AdsSymbolEntry, typeLength)>,
                ref_property<ref_prop::commentLen, ELEMENT(AdsSymbolEntry, commentLength)>,
                ref_property<ref_prop::nameData, next_data<AdsSymbolEntry, ref_prop::commentLen>>,
                ref_property<ref_prop::name, string<AdsSymbolEntry, ref_prop::nameData, ref_prop::nameLen>>,
                ref_property<ref_prop::nameTerminator, next_element_<AdsSymbolEntry, ref_prop::name, char, '\0'>>,
                ref_property<ref_prop::typeData, next_data<AdsSymbolEntry, ref_prop::nameTerminator>>,
                ref_property<ref_prop::type, string<AdsSymbolEntry, ref_prop::typeData, ref_prop::typeLen>>,
                ref_property<ref_prop::typeTerminator, next_element_<AdsSymbolEntry, ref_prop::type, char, '\0'>>,
                ref_property<ref_prop::commentData, next_data<AdsSymbolEntry, ref_prop::typeTerminator>>,
                ref_property<ref_prop::comment, string<AdsSymbolEntry, ref_prop::commentData, ref_prop::commentLen>>,
                ref_property<ref_prop::commentTerminator, next_element_<AdsSymbolEntry, ref_prop::comment, char, '\0'>>,
                ref_property<ref_prop::hasGuid, flag<AdsSymbolEntry, ref_prop::flags, ADSSYMBOLFLAG_TYPEGUID>>,
                ref_property<ref_prop::guid, optional<AdsSymbolEntry, ref_prop::hasGuid, next_element<AdsSymbolEntry, ref_prop::commentTerminator, GUID>>>,
                ref_property<ref_prop::hasAttributes, flag<AdsSymbolEntry, ref_prop::flags, ADSSYMBOLFLAG_ATTRIBUTES>>,
                ref_property<ref_prop::numAttributes, optional_<AdsSymbolEntry, ref_prop::hasAttributes, next_element<AdsSymbolEntry, ref_prop::guid, ADS_UINT16>, 0>>,
                ref_property<ref_prop::attributeData, array<AdsSymbolEntry, ref_prop::numAttributes, ref_prop::numAttributes, AdsAttributeEntry>>,
    //            ref_property<ref_prop::expEntryLen, next_data<AdsSymbolEntry, ref_prop::attributeData, ref_prop::entryLen>>,
                ref_property<ref_prop::isStatic, flag<AdsSymbolEntry, ref_prop::flags, ADSSYMBOLFLAG_STATIC>>>
            {
                static constexpr ref_prop sort_prop = ref_prop::name;
                static constexpr auto cmp_less = icmp_less;
                static constexpr auto cmp_equal = icmp_equal;
            };
    
            template <>
            struct properties_of<AdsAttributeEntry>
                : structured_type<
                ref_property<ref_prop::nameLen, ELEMENT(AdsAttributeEntry, nameLength)>,
                ref_property<ref_prop::valueLen, ELEMENT(AdsAttributeEntry, valueLength)>,
                ref_property<ref_prop::nameData, next_data<AdsAttributeEntry, ref_prop::valueLen>>,
                ref_property<ref_prop::name, string<AdsAttributeEntry, ref_prop::nameData, ref_prop::nameLen>>,
                ref_property<ref_prop::nameTerminator, next_element_<AdsAttributeEntry, ref_prop::name, char, '\0'>>,
                ref_property<ref_prop::valueData, next_data<AdsAttributeEntry, ref_prop::nameTerminator>>,
                ref_property<ref_prop::value, string<AdsAttributeEntry, ref_prop::valueData, ref_prop::valueLen>>,
                ref_property<ref_prop::valueTerminator, next_element_<AdsAttributeEntry, ref_prop::value, char, '\0'>>,
                ref_property<ref_prop::entryLen, next_data<AdsAttributeEntry, ref_prop::valueTerminator>>>
            {};
    
            template <>
            struct properties_of<AdsDatatypeEntry>
                : structured_type<
                ref_property<ref_prop::typeSize, fixed_size<  AdsDatatypeEntry>>,
                ref_property<ref_prop::entryLen, ELEMENT(AdsDatatypeEntry, entryLength)>,
                ref_property<ref_prop::version, ELEMENT_EXP(AdsDatatypeEntry, version, 1)>,
                ref_property<ref_prop::size, ELEMENT(AdsDatatypeEntry, size)>,
                ref_property<ref_prop::offset, ELEMENT(AdsDatatypeEntry, offs)>,
                ref_property<ref_prop::typeId, ELEMENT_TYPE(AdsDatatypeEntry, dataType, adsDatatypeId)>,
                ref_property<ref_prop::flags, ELEMENT(AdsDatatypeEntry, flags)>,
                ref_property<ref_prop::nameLen, ELEMENT(AdsDatatypeEntry, nameLength)>,
                ref_property<ref_prop::typeLen, ELEMENT(AdsDatatypeEntry, typeLength)>,
                ref_property<ref_prop::commentLen, ELEMENT(AdsDatatypeEntry, commentLength)>,
                ref_property<ref_prop::arrayDim, ELEMENT(AdsDatatypeEntry, arrayDim)>,
                ref_property<ref_prop::subItems, ELEMENT(AdsDatatypeEntry, subItems)>,
                ref_property<ref_prop::nameData, next_data<AdsDatatypeEntry, ref_prop::subItems>>,
                ref_property<ref_prop::name, string<AdsDatatypeEntry, ref_prop::nameData, ref_prop::nameLen>>,
                ref_property<ref_prop::nameTerminator, next_element_<AdsDatatypeEntry, ref_prop::name, char, '\0'>>,
                ref_property<ref_prop::typeData, next_data<AdsDatatypeEntry, ref_prop::nameTerminator>>,
                ref_property<ref_prop::type, string<AdsDatatypeEntry, ref_prop::typeData, ref_prop::typeLen>>,
                ref_property<ref_prop::typeTerminator, next_element_<AdsDatatypeEntry, ref_prop::type, char, '\0'>>,
                ref_property<ref_prop::commentData, next_data<AdsDatatypeEntry, ref_prop::typeTerminator>>,
                ref_property<ref_prop::comment, string<AdsDatatypeEntry, ref_prop::commentData, ref_prop::commentLen>>,
                ref_property<ref_prop::commentTerminator, next_element_<AdsDatatypeEntry, ref_prop::comment, char, '\0'>>,
                ref_property<ref_prop::arrayInfo, array<AdsDatatypeEntry, ref_prop::commentTerminator, ref_prop::arrayDim, AdsDatatypeArrayInfo>>,
                ref_property<ref_prop::subItemData, array<AdsDatatypeEntry, ref_prop::arrayInfo, ref_prop::subItems, AdsDatatypeEntry>>,
                ref_property<ref_prop::hasGuid, flag<AdsDatatypeEntry, ref_prop::flags, ADSDATATYPEFLAG_TYPEGUID>>,
                ref_property<ref_prop::guid, optional<AdsDatatypeEntry, ref_prop::hasGuid, next_element<AdsDatatypeEntry, ref_prop::subItemData, GUID>>>,
                ref_property<ref_prop::hasCopyMask, flag<AdsDatatypeEntry, ref_prop::flags, ADSDATATYPEFLAG_COPYMASK>>,
                ref_property<ref_prop::copyMaskLen, optional_<AdsDatatypeEntry, ref_prop::hasCopyMask, ELEMENT(AdsDatatypeEntry, size), 0>>,
                ref_property<ref_prop::copyMask, array<AdsDatatypeEntry, ref_prop::guid, ref_prop::copyMaskLen, ADS_UINT8>>,
                ref_property<ref_prop::hasMethods, flag<AdsDatatypeEntry, ref_prop::flags, ADSDATATYPEFLAG_METHODINFOS>>,
                ref_property<ref_prop::methods, optional_<AdsDatatypeEntry, ref_prop::hasMethods, next_element<AdsDatatypeEntry, ref_prop::copyMask, ADS_UINT16>, 0>>,
                ref_property<ref_prop::methodData, array<AdsDatatypeEntry, ref_prop::methods, ref_prop::methods, AdsMethodEntry>>,
                ref_property<ref_prop::hasAttributes, flag<AdsDatatypeEntry, ref_prop::flags, ADSDATATYPEFLAG_ATTRIBUTES>>,
                ref_property<ref_prop::numAttributes, optional_<AdsDatatypeEntry, ref_prop::hasAttributes, next_element<AdsDatatypeEntry, ref_prop::methodData, ADS_UINT16>, 0>>,
                ref_property<ref_prop::attributeData, array<AdsDatatypeEntry, ref_prop::numAttributes, ref_prop::numAttributes, AdsAttributeEntry>>,
                ref_property<ref_prop::hasEnumInfos, flag<AdsDatatypeEntry, ref_prop::flags, ADSDATATYPEFLAG_ENUMINFOS>>,
                ref_property<ref_prop::enumInfos, optional_<AdsDatatypeEntry, ref_prop::hasEnumInfos, next_element<AdsDatatypeEntry, ref_prop::attributeData, ADS_UINT16>, 0>>,
                ref_property<ref_prop::enums, array<AdsDatatypeEntry, ref_prop::enumInfos, ref_prop::enumInfos, AdsEnumInfoEntry>>,
    //            ref_property<ref_prop::expEntryLen, next_data<AdsDatatypeEntry, ref_prop::enums, ref_prop::entryLen>>,
                ref_property<ref_prop::isStatic, flag<AdsDatatypeEntry, ref_prop::flags, ADSDATATYPEFLAG_STATIC>>>
            {
                static constexpr ref_prop sort_prop = ref_prop::name;
                static constexpr auto cmp_less = std::less<>{};
                static constexpr auto cmp_equal = std::equal_to<>{};
            };
    
            template <>
            struct properties_of<AdsMethodParaEntry>
                : structured_type<
                ref_property<ref_prop::entryLen, ELEMENT(AdsMethodParaEntry, entryLength)>,
                ref_property<ref_prop::size, ELEMENT(AdsMethodParaEntry, size)>,
                ref_property<ref_prop::alignSize, ELEMENT(AdsMethodParaEntry, alignSize)>,
                ref_property<ref_prop::typeId, ELEMENT_TYPE(AdsMethodParaEntry, dataType, adsDatatypeId)>,
                ref_property<ref_prop::flags, ELEMENT(AdsMethodParaEntry, flags)>,
                ref_property<ref_prop::reserved, ELEMENT_EXP(AdsMethodParaEntry, reserved, 0)>,
                ref_property<ref_prop::guid, ELEMENT(AdsMethodParaEntry, typeGuid)>,
                ref_property<ref_prop::lengthIsPara, ELEMENT(AdsMethodParaEntry, lengthIsPara)>,
                ref_property<ref_prop::nameLen, ELEMENT(AdsMethodParaEntry, nameLength)>,
                ref_property<ref_prop::typeLen, ELEMENT(AdsMethodParaEntry, typeLength)>,
                ref_property<ref_prop::commentLen, ELEMENT(AdsMethodParaEntry, commentLength)>,
                ref_property<ref_prop::nameData, next_data<AdsMethodParaEntry, ref_prop::commentLen>>,
                ref_property<ref_prop::name, string<AdsMethodParaEntry, ref_prop::nameData, ref_prop::nameLen>>,
                ref_property<ref_prop::nameTerminator, next_element_<AdsMethodParaEntry, ref_prop::name, char, '\0'>>,
                ref_property<ref_prop::typeData, next_data<AdsMethodParaEntry, ref_prop::nameTerminator>>,
                ref_property<ref_prop::type, string<AdsMethodParaEntry, ref_prop::typeData, ref_prop::typeLen>>,
                ref_property<ref_prop::typeTerminator, next_element_<AdsMethodParaEntry, ref_prop::type, char, '\0'>>,
                ref_property<ref_prop::commentData, next_data<AdsMethodParaEntry, ref_prop::typeTerminator>>,
                ref_property<ref_prop::comment, string<AdsMethodParaEntry, ref_prop::commentData, ref_prop::commentLen>>,
                ref_property<ref_prop::commentTerminator, next_element_<AdsMethodParaEntry, ref_prop::comment, char, '\0'>>,
                ref_property<ref_prop::expEntryLen, next_data<AdsMethodParaEntry, ref_prop::commentTerminator, ref_prop::entryLen>>>
            {};
    
            template <>
            struct properties_of<AdsMethodEntry>
                : structured_type<
                ref_property<ref_prop::entryLen, ELEMENT(AdsMethodEntry, entryLength)>,
                ref_property<ref_prop::version, ELEMENT_EXP(AdsMethodEntry, version, 1)>,
                ref_property<ref_prop::vTableIndex, ELEMENT(AdsMethodEntry, vTableIndex)>,
                ref_property<ref_prop::size, ELEMENT(AdsMethodEntry, returnSize)>,
                ref_property<ref_prop::alignSize, ELEMENT(AdsMethodEntry, returnAlignSize)>,
                ref_property<ref_prop::reserved, ELEMENT_EXP(AdsMethodEntry, reserved, 0)>,
                ref_property<ref_prop::guid, ELEMENT(AdsMethodEntry, returnTypeGuid)>,
                ref_property<ref_prop::typeId, ELEMENT_TYPE(AdsMethodEntry, returnDataType, adsDatatypeId)>,
                ref_property<ref_prop::flags, ELEMENT(AdsMethodEntry, flags)>,
                ref_property<ref_prop::nameLen, ELEMENT(AdsMethodEntry, nameLength)>,
                ref_property<ref_prop::typeLen, ELEMENT(AdsMethodEntry, returnTypeLength)>,
                ref_property<ref_prop::commentLen, ELEMENT(AdsMethodEntry, commentLength)>,
                ref_property<ref_prop::paras, ELEMENT(AdsMethodEntry, paras)>,
                ref_property<ref_prop::nameData, next_data<AdsMethodEntry, ref_prop::paras>>,
                ref_property<ref_prop::name, string<AdsMethodEntry, ref_prop::nameData, ref_prop::nameLen>>,
                ref_property<ref_prop::nameTerminator, next_element_<AdsMethodEntry, ref_prop::name, char, '\0'>>,
                ref_property<ref_prop::typeData, next_data<AdsMethodEntry, ref_prop::nameTerminator>>,
                ref_property<ref_prop::type, string<AdsMethodEntry, ref_prop::typeData, ref_prop::typeLen>>,
                ref_property<ref_prop::typeTerminator, next_element_<AdsMethodEntry, ref_prop::type, char, '\0'>>,
                ref_property<ref_prop::commentData, next_data<AdsMethodEntry, ref_prop::typeTerminator>>,
                ref_property<ref_prop::comment, string<AdsMethodEntry, ref_prop::commentData, ref_prop::commentLen>>,
                ref_property<ref_prop::commentTerminator, next_element_<AdsMethodEntry, ref_prop::comment, char, '\0'>>,
                ref_property<ref_prop::paraData, array<AdsMethodEntry, ref_prop::commentTerminator, ref_prop::paras, AdsMethodParaEntry>>,
                ref_property<ref_prop::expEntryLen, next_data<AdsMethodEntry, ref_prop::paraData, ref_prop::entryLen>>>
            {};
    
            template <>
            struct properties_of<AdsEnumInfoEntry>
                : structured_type<
                ref_property<ref_prop::nameLen, ELEMENT(AdsEnumInfoEntry, nameLength)>,
                ref_property<ref_prop::nameData, next_data<AdsEnumInfoEntry, ref_prop::nameLen>>,
                ref_property<ref_prop::name, string<AdsEnumInfoEntry, ref_prop::nameData, ref_prop::nameLen>>,
                ref_property<ref_prop::nameTerminator, next_element_<AdsEnumInfoEntry, ref_prop::name, char, '\0'>>,
                ref_property<ref_prop::value, var_int<AdsEnumInfoEntry, ref_prop::nameTerminator, AdsDatatypeEntry, ref_prop::size>>,
                ref_property<ref_prop::entryLen, next_data<AdsEnumInfoEntry, ref_prop::value>>>
            {};
    
            template <typename T>
            class ref {
            public:
                constexpr explicit ref(const char* p) noexcept : data(p) {}
                ref(const char* p, std::size_t entryLimit) : data(p) {
                    properties_of<T>::check(p, nullptr, entryLimit);
                }
                template <ref_prop prop>
                auto get() const noexcept {
                    return get<prop>(data);
                }
                template <ref_prop prop>
                static decltype(auto) get(const char*p) noexcept {
                    return properties_of<T>::template get<prop>(p, nullptr);
                }
            private:
                const char* data;
    
                friend bool operator<(const ref& lhs, const ref& rhs) noexcept {
                    using prop = properties_of<T>;
                    return prop::cmp_less(lhs.get<prop::sort_prop>(), rhs.get<prop::sort_prop>());
                }
                template <typename U>
                friend bool operator<(const U& lhs, const ref& rhs) noexcept {
                    using prop = properties_of<T>;
                    return prop::cmp_less(lhs, rhs.get<prop::sort_prop>());
                }
                template <typename U>
                friend bool operator<(const ref& lhs, const U& rhs) noexcept {
                    using prop = properties_of<T>;
                    return prop::cmp_less(lhs.get<prop::sort_prop>(), rhs);
                }
                friend bool operator==(const ref& lhs, const ref& rhs) noexcept {
                    using prop = properties_of<T>;
                    return prop::cmp_equal(lhs.get<prop::sort_prop>(), rhs.get<prop::sort_prop>());
                }
                template <typename U>
                friend bool operator==(const U& lhs, const ref& rhs) noexcept {
                    using prop = properties_of<T>;
                    return prop::cmp_equal(lhs, rhs.get<prop::sort_prop>());
                }
                template <typename U>
                friend bool operator==(const ref& lhs, const U& rhs) noexcept {
                    using prop = properties_of<T>;
                    return prop::cmp_equal(lhs.get<prop::sort_prop>(), rhs);
                }
            };
        } // namespace detail
        using detail::ref;
        using detail::ref_prop;
    
        inline std::size_t checkedSum(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;
        }
    
        using std::rbegin;
        using std::rend;
    
        template <typename T>
        struct reverse_view { T& iterable; };
    
        template <typename T>
        auto begin(reverse_view<T> w) { return rbegin(w.iterable); }
    
        template <typename T>
        auto end(reverse_view<T> w) { return rend(w.iterable); }
    
        template <typename T>
        reverse_view<T> reverse(T&& iterable) { return { iterable }; }
    }
    
    class adsData::subSymbolInfo {
    public:
        subSymbolInfo(std::string_view name, std::string_view comment,
            sizeType offset, bool isStatic,
            adsData::datatypeInfo* typeData = nullptr)
            : name{ name }, comment{ comment }, offset{ offset },
              isStatic { isStatic }, typeData{ typeData }
        {}
        std::string name;
        std::string comment;
        sizeType offset;
        bool isStatic;
        adsData::datatypeInfo* typeData;
        friend bool operator==(const subSymbolInfo& lhs, const subSymbolInfo& rhs) {
            return icmp_equal(lhs.name, rhs.name);
        }
        friend bool operator==(const subSymbolInfo& lhs, std::string_view rhs) {
            return icmp_equal(lhs.name, rhs);
        }
        friend bool operator==(std::string_view lhs, const subSymbolInfo& rhs) {
            return icmp_equal(lhs, rhs.name);
        }
    };
    
    namespace {
    
        struct adsArrayInfo : adsData::subSymbolInfo
        {
            adsArrayInfo(std::string_view comment, sizeType arraySize, sizeType arrayDims, const char* arrayInfoData)
                : adsData::subSymbolInfo{ ""sv, comment, 0, false }, arrayInfo{}, numElements_{ 1 }
            {
                assert(arrayDims != 0);
                arrayInfo.reserve(arrayDims);
                for (; arrayDims--; ) {
                    ref<AdsDatatypeArrayInfo> ref(arrayInfoData);
                    auto& info = arrayInfo.emplace_back(AdsDatatypeArrayInfo{ ref.get<ref_prop::lBound>(), ref.get<ref_prop::elements>() });
                    sizeType rBound = info.lBound + info.elements - 1;
                    if (info.elements == 0 || rBound < info.lBound || arraySize % info.elements != 0)
                        throw std::runtime_error("array info corrupted");
                    arraySize /= info.elements;
                    numElements_ *= info.elements;
                    arrayInfoData += ref.get<ref_prop::entryLen>();
                }
            }
            sizeType index(std::string_view i) const {
                auto workIndex = i;
                sizeType realIndex = 0;
                for (auto& info : arrayInfo) {
                    auto pos = workIndex.find(',');
                    if ((&info == &arrayInfo.back()) == (pos == workIndex.npos))
                        throw std::out_of_range("index with wrong number of dimensions: "s + i);
                    auto curIndex = workIndex;
                    if (pos != workIndex.npos) {
                        curIndex.remove_suffix(curIndex.size() - pos);
                        workIndex.remove_prefix(pos + 1);
                    }
                    auto n = svtoi(curIndex);
                    if (n < info.lBound || n - info.lBound >= info.elements)
                        throw std::out_of_range("index out of range: "s + i);
                    // we don't need to check for overflow here since the constructor already ensures that 
                    // indizes stay within proper bounds
                    realIndex = realIndex * info.elements + (n - info.lBound);
                }
                return realIndex;
            }
            std::string toString(sizeType i) const {
                if (i >= numElements_)
                    throw std::out_of_range("index out of range");
                std::string result = "]";
                for (auto& info : reverse(arrayInfo)) {
                    auto curIndex = i % info.elements;
                    i /= info.elements;
                    do {
                        result.push_back(static_cast<char>('0' + curIndex % 10));
                    } while (curIndex /= 10);
                    if (&info != &arrayInfo.front())
                        result.push_back(',');
                }
                result.push_back('[');
                std::reverse(result.begin(), result.end());
                return result;
            }
            sizeType elemSize() const noexcept;
            sizeType numElements() const noexcept { return numElements_; }
            static sizeType svtoi(std::string_view s) {
                sizeType result = 0;
                if (s.empty())
                    throw std::out_of_range("index corrupted");
                for (; !s.empty(); s.remove_prefix(1)) {
                    if (s[0] < '0' || s[0] > '9' || (std::numeric_limits<sizeType>::max() - (s[0] - '0')) / 10 < result)
                        throw std::out_of_range("index corrupted");
                    result = result * 10 + (s[0] - '0');
                }
                return result;
            }
            std::vector<AdsDatatypeArrayInfo> arrayInfo;
            sizeType numElements_;
        };
    }
    
    class adsData::symbolInfo {
    public:
        symbolInfo(std::string_view name, std::string_view comment,
            sizeType offset, sizeType group, bool isStatic, datatypeInfo* typeData )
            : baseInfo{ name, comment, offset, isStatic, typeData }, group{ group }
        {}
        subSymbolInfo baseInfo;
        sizeType group;
        friend bool operator<(const symbolInfo& lhs, const symbolInfo& rhs) {
            return icmp_less(lhs.baseInfo.name, rhs.baseInfo.name);
        }
        friend bool operator<(const symbolInfo& lhs, std::string_view rhs) {
            return icmp_less(lhs.baseInfo.name, rhs);
        }
        friend bool operator<(std::string_view lhs, const symbolInfo& rhs) {
            return icmp_less(lhs, rhs.baseInfo.name);
        }
    };
    class adsData::datatypeInfo {
    public:
        datatypeInfo(std::string_view name, sizeType size, adsDatatypeId id, std::optional<GUID> guid)
            : name{ name }, size{ size }, guid{ guid }, id { id }, visited(false),
              typeSpecs{}
        {}
        datatypeInfo(std::string_view name, sizeType size, adsDatatypeId id, std::optional<GUID> guid,
            datatypeInfo* info)
            : name{ name }, size{ size }, guid{ guid }, id{ id }, visited(false),
              typeSpecs{ info }
        {}
        datatypeInfo(std::string_view name, sizeType size, adsDatatypeId id, std::optional<GUID> guid,
            adsArrayInfo&& arrayInfo)
            : name{ name }, size{ size }, guid{ guid }, id{ id }, visited(false),
              typeSpecs{ std::move(arrayInfo) }
        {}
        datatypeInfo(std::string_view name, sizeType size, adsDatatypeId id, std::optional<GUID> guid,
            sizeType numSymbols)
            : name{ name }, size{ size }, guid{ guid }, id{ id }, visited(false),
              typeSpecs{ std::vector<subSymbolInfo>{} }
        {
            std::get<std::vector<subSymbolInfo>>(typeSpecs).reserve(numSymbols);
        }
        std::string name;
        sizeType size;
        std::optional<GUID> guid;
        adsDatatypeId id;
        bool visited; // used for cycle detection
        enum { baseSpec, compoundSpec, arraySpec, classSpec };
        std::variant<std::monostate,                         // base type
                     adsData::datatypeInfo*,                 // compound type
                     adsArrayInfo,                           // array
                     std::vector<subSymbolInfo>> typeSpecs;  // class
    
        friend bool operator<(const adsData::datatypeInfo& lhs, const adsData::datatypeInfo& rhs) {
            return lhs.name < rhs.name;
        }
        friend bool operator<(const adsData::datatypeInfo& lhs, std::string_view rhs) {
            return lhs.name < rhs;
        }
        friend bool operator<(std::string_view lhs, const adsData::datatypeInfo& rhs) {
            return lhs < rhs.name;
        }
    };
    namespace {
        sizeType adsArrayInfo::elemSize() const noexcept { return typeData->size; }
    
        using adsDT = ref<AdsDatatypeEntry>;
        using adsSym = ref<AdsSymbolEntry>;
    
        template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
        std::string to_string_hex(T v) {
            auto val = static_cast<std::uint64_t>(v);
            char buf[2 * sizeof(val) + 1];
            std::snprintf(buf, sizeof(buf), "%llx", val);
            return buf;
        }
    
        const adsDT* find(const std::vector<adsDT>& data, std::string_view name) noexcept {
            auto it = std::lower_bound(data.begin(), data.end(), name);
            return it != data.end() && it->get<ref_prop::name>() == name ? &*it : nullptr;
        }
        const adsSym* find(const std::vector<adsSym>& data, std::string_view name) noexcept {
            auto it = std::upper_bound(data.begin(), data.end(), name);
            // prev is a match if it is a proper prefix of name
            return it != data.begin() && (--it)->get<ref_prop::nameLen>() <= name.size()
                && icmp_equal(it->get<ref_prop::name>(), std::string_view{ 0, it->get<ref_prop::nameLen>() }) ? &*it : nullptr;
        }
    
        void addDatatypes(const std::vector<char>& dtData, std::vector<adsData::datatypeInfo>& types) {
            auto limit = dtData.size();
            std::size_t count = 0;
            for (auto p = &dtData[0]; limit != 0; ) {
                adsDT cur(p, limit);
                ++count;
                p += cur.get<ref_prop::entryLen>();
                limit -= cur.get<ref_prop::entryLen>();
            }
            types.reserve(count);
            for (auto p = &dtData[0]; count--; ) {
                adsDT dt(p);
                p += dt.get<ref_prop::entryLen>();
                if (dt.get<ref_prop::arrayDim>() != 0) {
                    types.emplace_back(dt.get<ref_prop::name>(), dt.get<ref_prop::size>(), dt.get<ref_prop::typeId>(), dt.get<ref_prop::guid>(),
                        adsArrayInfo{ dt.get<ref_prop::comment>(), dt.get<ref_prop::size>(), dt.get<ref_prop::arrayDim>(), dt.get<ref_prop::arrayInfo>() });
                } else if (dt.get<ref_prop::subItems>() != 0) {
                    auto& ref = types.emplace_back(dt.get<ref_prop::name>(), dt.get<ref_prop::size>(), dt.get<ref_prop::typeId>(),
                        dt.get<ref_prop::guid>(), dt.get<ref_prop::subItems>());
                    auto& subs = std::get<adsData::datatypeInfo::classSpec>(ref.typeSpecs);
                    auto q = dt.get<ref_prop::subItemData>();
                    for (auto n = dt.get<ref_prop::subItems>(); n--;) {
                        adsDT sub(q);
                        q += sub.get<ref_prop::entryLen>();
                        subs.emplace_back(sub.get<ref_prop::name>(), sub.get<ref_prop::comment>(), sub.get<ref_prop::offset>(),
                            sub.get<ref_prop::isStatic>());
                    }
                } else if (dt.get<ref_prop::type>().empty()) {
                    types.emplace_back(dt.get<ref_prop::name>(), dt.get<ref_prop::size>(), dt.get<ref_prop::typeId>(), dt.get<ref_prop::guid>());
                } else {
                    types.emplace_back(dt.get<ref_prop::name>(), dt.get<ref_prop::size>(), dt.get<ref_prop::typeId>(), dt.get<ref_prop::guid>(), nullptr );
                }
            }
            std::sort(types.begin(), types.end());
        }
        void linkDatatypes(const std::vector<char>& dtData, std::vector<adsData::datatypeInfo>& types) {
            auto p = &dtData[0];
            for (auto count = types.size(); count--; ) {
                adsDT dt(p);
                p += dt.get<ref_prop::entryLen>();
                auto type = std::lower_bound(types.begin(), types.end(), dt.get<ref_prop::name>());
                assert(type != types.end());
                switch (type->typeSpecs.index()) {
                    default:
                        assert(false);
                    case adsData::datatypeInfo::baseSpec:
                        break;
                    case adsData::datatypeInfo::compoundSpec: {
                        auto it = std::lower_bound(types.begin(), types.end(), dt.get<ref_prop::type>());
                        if (it == types.end())
                            throw std::runtime_error("Datytypes corrupted: missing type data");
                        std::get<adsData::datatypeInfo::compoundSpec>(type->typeSpecs) = &*it;
                        break;
                    }
                    case adsData::datatypeInfo::arraySpec: {
                        auto it = std::lower_bound(types.begin(), types.end(), dt.get<ref_prop::type>());
                        if (it == types.end())
                            throw std::runtime_error("Datytypes corrupted: missing type data");
                        auto& info = std::get<adsData::datatypeInfo::arraySpec>(type->typeSpecs);
                        if (dt.get<ref_prop::size>() / info.numElements() != it->size)
                            throw std::runtime_error("Datatypes corrupted: mismatched element size");
                        info.typeData = &*it;
                        break;
                    }
                    case adsData::datatypeInfo::classSpec: {
                        const char* q = dt.get<ref_prop::subItemData>();
                        for (auto& info : std::get<adsData::datatypeInfo::classSpec>(type->typeSpecs)) {
                            adsDT sub(q);
                            q += sub.get<ref_prop::entryLen>();
                            auto it = std::lower_bound(types.begin(), types.end(), sub.get<ref_prop::type>());
                            if (it == types.end())
                                throw std::runtime_error("Datytypes corrupted: missing type data");
                            if (sub.get<ref_prop::size>() != it->size)
                                throw std::runtime_error("Datatypes corrupted: alias mismatched size");
                            info.typeData = &*it;
                        }
                        break;
                    }
                }
            }
        }
        void addSymbols(const std::vector<char>& symData, std::vector<adsData::datatypeInfo>& types,
            std::vector<adsData::symbolInfo>& symbols) {
            auto limit = symData.size();
            std::size_t count = 0;
            for (auto p = &symData[0]; limit != 0; ) {
                adsSym cur(p, limit);
                ++count;
                p += cur.get<ref_prop::entryLen>();
                limit -= cur.get<ref_prop::entryLen>();
            }
            symbols.reserve(symData.size());
            for (auto p = &symData[0]; count--; ) {
                adsSym sym(p);
                p += sym.get<ref_prop::entryLen>();
                if (sym.get<ref_prop::type>().empty())
                    throw std::runtime_error("Symboldata corrupted: symbol has no type");
                auto it = std::lower_bound(types.begin(), types.end(), sym.get<ref_prop::type>());
                if (it == types.end())
                    throw std::runtime_error("Symboldata corrupted: missing type data");
                if (sym.get<ref_prop::size>() != it->size)
                    throw std::runtime_error("Symboldata corrupted: mismatched size");
                if (sym.get<ref_prop::guid>() != it->guid)
                    throw std::runtime_error("mismatched guid");
                symbols.emplace_back(sym.get<ref_prop::name>(), sym.get<ref_prop::comment>(), sym.get<ref_prop::offset>(),
                    sym.get<ref_prop::group>(), sym.get<ref_prop::isStatic>(), &*it);
            }
            std::sort(symbols.begin(), symbols.end());
        }
        void cycleCheck(adsData::datatypeInfo* p) {
            if (p->visited)
                throw std::runtime_error("Datatypes corrupted: cycle detected");
            switch (p->typeSpecs.index()) {
                default:
                    assert(false);
                case adsData::datatypeInfo::baseSpec:
                    return;
                case adsData::datatypeInfo::compoundSpec:
                    p->visited = true;
                    cycleCheck(std::get<adsData::datatypeInfo::compoundSpec>(p->typeSpecs));
                    p->visited = false;
                    return;
                case adsData::datatypeInfo::arraySpec: {
                    auto& info = std::get<adsData::datatypeInfo::arraySpec>(p->typeSpecs);
                    if (info.numElements() != 1)
                        return;
                    p->visited = true;
                    cycleCheck(info.typeData);
                    p->visited = false;
                    return;
                }
                case adsData::datatypeInfo::classSpec: {
                    auto& info = std::get<adsData::datatypeInfo::classSpec>(p->typeSpecs);
                    p->visited = true;
                    for (auto& sub : info)
                        if (sub.typeData->size == p->size)
                            cycleCheck(sub.typeData);
                    p->visited = false;
                    return;
                }
            }
        }
        void cycleCheck(std::vector<adsData::datatypeInfo>& types) {
            for (auto& info : types)
                cycleCheck(&info);
        }
    } // namespace
    
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    adsData::adsData(decltype(AmsAddr::port) port)
        : symbols_{}, types_{}
    {
        auto adsPort = tcAdsAPI::AdsPortOpenEx();
        if (adsPort == 0)
            throw std::runtime_error("cannot open ads port");
        std::unique_ptr<decltype(adsPort), void(*)(decltype(adsPort)*)> guard(&adsPort,
            [](decltype(adsPort)* p) { tcAdsAPI::AdsPortCloseEx(*p); });
        tcAdsAPI::AmsAddr ams_addr{};
        if (auto err = tcAdsAPI::AdsGetLocalAddressEx(adsPort, &ams_addr); err != ADSERR_NOERR)
            throw std::runtime_error("cannot get local ams address: 0x" + to_string_hex(err));
        ams_addr.port = port;
        AdsSymbolUploadInfo2 info{};
        unsigned long bytes_read = 0;
        if (auto err = tcAdsAPI::AdsSyncReadReqEx2(adsPort, &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");
        std::vector<char> symData(info.nSymSize);
        std::vector<char> dtData(info.nDatatypeSize);
        if (auto err = tcAdsAPI::AdsSyncReadReqEx2(adsPort, &ams_addr, ADSIGRP_SYM_UPLOAD, 0, info.nSymSize,
            &symData[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 = tcAdsAPI::AdsSyncReadReqEx2(adsPort, &ams_addr, ADSIGRP_SYM_DT_UPLOAD, 0, info.nDatatypeSize,
            &dtData[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");
        guard.release();
        initialize(symData, dtData);
    }
    
    adsData::adsData(adsData&&) noexcept = default;
    adsData& adsData::operator=(adsData&&) noexcept = default;
    
    adsData::~adsData() noexcept = default;
    
    void adsData::initialize(const std::vector<char>& symData, const std::vector<char>& dtData) {
        addDatatypes(dtData, types_);
        linkDatatypes(dtData, types_);
        addSymbols(symData, types_, symbols_);
        cycleCheck(types_);
    }
    
    adsVarData adsData::operator[](std::string_view name) const {
        auto curName = name;
        if (auto pos = curName.find('['); pos != curName.npos)
            curName.remove_suffix(curName.size() - pos);
        auto sym = std::lower_bound(symbols_.begin(), symbols_.end(), curName);
        if (sym == symbols_.end())
            throw std::out_of_range("var not found: " + name);
        auto group = sym->group;
        auto offset = sym->baseInfo.offset;
        if (name.size() == sym->baseInfo.name.size())
            return adsVarData{ &sym->baseInfo, group, offset };
        curName = std::string_view{ name.data() + sym->baseInfo.name.size(), name.size() - sym->baseInfo.name.size() };
        if (curName[0] != '.' && curName[0] != '[')
            throw std::out_of_range("var not found: " + name);
        auto dt = sym->baseInfo.typeData;
        for (;;) {
            if (curName[0] == '.') {
                if (dt->typeSpecs.index() != adsData::datatypeInfo::classSpec)
                    throw std::out_of_range("has no subobjects: "s
                        + std::string_view{ name.data(), static_cast<std::size_t>(curName.data() - name.data()) }
                        + " with "sv + curName);
                curName.remove_prefix(1); // '.'
                auto shortName = curName;
                auto pos = shortName.find_first_of(".["sv);
                if (pos != shortName.npos) {
                    shortName.remove_suffix(shortName.size() - pos);
                }
                auto& subs = std::get<adsData::datatypeInfo::classSpec>(dt->typeSpecs);
                auto sub = std::find_if(subs.begin(), subs.end(), [=](auto& v) { return icmp_equal(shortName, v.name); });
                if (sub == subs.end())
                    throw std::out_of_range("subobjects not found: "s + shortName + " of "sv
                        + std::string_view{ name.data(), static_cast<std::size_t>(curName.data() - name.data()) }
                        + " with "sv + name);
                if (sub->isStatic)
                    offset = sub->offset;
                else
                    offset += sub->offset;
                if (pos == shortName.npos)
                    return adsVarData{ &*sub, group, offset };
                curName.remove_prefix(pos);
                dt = sub->typeData;
            } else {
                // cur_name[0] == '['
                if (dt->typeSpecs.index() != adsData::datatypeInfo::arraySpec)
                    throw std::out_of_range("is no array: "s
                        + std::string_view{ name.data(), static_cast<std::size_t>(curName.data() - name.data()) }
                +" with "sv + curName);
                curName.remove_prefix(1); // '['
                auto pos = curName.find(']');
                if (pos == curName.npos)
                    throw std::out_of_range("missing ]");
                auto index = curName;
                index.remove_suffix(index.size() - pos);
                auto& info = std::get<adsData::datatypeInfo::arraySpec>(dt->typeSpecs);
                auto i = info.index(index);
                offset += info.elemSize() * i;
                curName.remove_prefix(pos + 1); // "index]"
                if (curName.empty())
                    return adsVarData{ &info, group, offset, i };
                if (curName[0] != '[' && curName[0] != '.')
                    throw std::out_of_range("missing . or [");
                dt = info.typeData;
            }
        }
    }
    
    adsData::iterator adsData::begin() const noexcept { return iterator{ symbols_.begin() }; }
    adsData::iterator adsData::end() const noexcept { return iterator{ symbols_.end() }; }
    adsData::iterator adsData::cbegin() const noexcept { return begin(); }
    adsData::iterator adsData::cend() const noexcept { return end(); }
    
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    std::string adsVarData::name(std::string prefix) const {
        if (!info_->name.empty()) {
            prefix.reserve(prefix.size() + 1 + info_->name.size());
            prefix += '.';
            prefix += info_->name;
            return std::move(prefix);
        } else
            return std::move(prefix) + shortName();
    }
    std::string adsVarData::shortName() const {
        return info_->name.empty() ? static_cast<const adsArrayInfo*>(info_)->toString(index_) : info_->name;
    }
    const std::string& adsVarData::type() const noexcept { return info_->typeData->name; }
    const std::string& adsVarData::comment() const noexcept { return info_->comment; }
    adsDatatypeId adsVarData::typeId() const noexcept { return info_->typeData->id; }
    sizeType adsVarData::group() const noexcept { return group_;  }
    sizeType adsVarData::offset() const noexcept { return offset_; }
    sizeType adsVarData::size() const noexcept { return info_->typeData->size; }
    bool adsVarData::isStatic() const noexcept { return info_->isStatic; }
    const GUID* adsVarData::typeGuid() const noexcept {
        return info_->typeData->guid.has_value() ? &*info_->typeData->guid : nullptr;
    }
    
    bool adsVarData::is_scalar() const noexcept {
        return info_->typeData->typeSpecs.index() == adsData::datatypeInfo::baseSpec
            || info_->typeData->typeSpecs.index() == adsData::datatypeInfo::compoundSpec; }
    bool adsVarData::is_array()  const noexcept {
        return info_->typeData->typeSpecs.index() == adsData::datatypeInfo::arraySpec;
    }
    bool adsVarData::is_struct() const noexcept {
        return info_->typeData->typeSpecs.index() == adsData::datatypeInfo::classSpec; }
    bool adsVarData::empty() const noexcept {
        return is_scalar() || is_struct()
            && std::get<adsData::datatypeInfo::classSpec>(info_->typeData->typeSpecs).empty();
    }
    sizeType adsVarData::subElements() const noexcept {
        switch (info_->typeData->typeSpecs.index()) {
        default:
            assert(false);
        case adsData::datatypeInfo::baseSpec:
        case adsData::datatypeInfo::compoundSpec:
            return 0;
        case adsData::datatypeInfo::arraySpec:
            return std::get<adsData::datatypeInfo::arraySpec>(info_->typeData->typeSpecs).numElements();
        case adsData::datatypeInfo::classSpec:
            return std::get<adsData::datatypeInfo::classSpec>(info_->typeData->typeSpecs).size();
        }
    }
    adsVarData::iterator adsVarData::begin() const noexcept {
        switch (info_->typeData->typeSpecs.index()) {
            default:
                assert(false);
            case adsData::datatypeInfo::baseSpec:
            case adsData::datatypeInfo::compoundSpec:
                return iterator{ nullptr, 0, 0, 0 };
            case adsData::datatypeInfo::arraySpec: {
                auto& info = std::get<adsData::datatypeInfo::arraySpec>(info_->typeData->typeSpecs);
                return iterator{ &info, group_, offset_, 0 };
            }
            case adsData::datatypeInfo::classSpec: {
                auto& info = std::get<adsData::datatypeInfo::classSpec>(info_->typeData->typeSpecs);
                return iterator{ info.empty() ? nullptr : &info.front(), group_, offset_, 0 };
            }
        }
    }
    adsVarData::iterator adsVarData::end() const noexcept {
        switch (info_->typeData->typeSpecs.index()) {
            default:
                assert(false);
            case adsData::datatypeInfo::baseSpec:
            case adsData::datatypeInfo::compoundSpec:
                return iterator{ nullptr, 0, 0, 0 };
            case adsData::datatypeInfo::arraySpec: {
                auto& info = std::get<adsData::datatypeInfo::arraySpec>(info_->typeData->typeSpecs);
                return iterator{ &info, group_, offset_, info.numElements() };
            }
            case adsData::datatypeInfo::classSpec: {
                auto& info = std::get<adsData::datatypeInfo::classSpec>(info_->typeData->typeSpecs);
                return iterator{ info.empty() ? nullptr : &info.back() + 1, group_, offset_, 0 };
            }
        }
    }
    adsVarData::iterator adsVarData::cbegin() const noexcept { return begin(); }
    adsVarData::iterator adsVarData::cend() const noexcept { return end(); }
    
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    adsData::iterator::reference adsData::iterator::operator*() const noexcept {
        return adsVarData{ &iter_->baseInfo, iter_->group, iter_->baseInfo.offset };
    }
    adsData::iterator::pointer adsData::iterator::operator->() const noexcept { return **this; }
    bool operator==(adsData::iterator lhs, adsData::iterator rhs) noexcept { return lhs.iter_ == rhs.iter_; }
    bool operator!=(adsData::iterator lhs, adsData::iterator rhs) noexcept { return lhs.iter_ != rhs.iter_; }
    adsData::iterator& adsData::iterator::operator++() noexcept { ++iter_; return *this; }
    adsData::iterator adsData::iterator::operator++(int) noexcept { auto tmp = *this; ++*this; return tmp; }
    adsData::iterator::iterator(std::vector<adsData::symbolInfo>::const_iterator it) noexcept : iter_{ it } {}
    

  • Mod

    adsData.cpp Teil 2

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    adsVarData::iterator::reference adsVarData::iterator::operator*() const noexcept {
        if (info_->name.empty())
            return adsVarData{ info_, group_, offset_ + index_ * info_->typeData->size, index_ };
        else
            return adsVarData{ info_, group_, info_->isStatic ? offset_ + info_->offset : info_->offset };
    }
    adsVarData::iterator::pointer adsVarData::iterator::operator->() const noexcept { return **this; }
    bool operator==(const adsVarData::iterator& lhs, const adsVarData::iterator& rhs) noexcept {
        return lhs.info_ == rhs.info_ && lhs.index_ == rhs.index_
            && lhs.offset_ == rhs.offset_ && lhs.group_ == rhs.group_;
    }
    bool operator!=(const adsVarData::iterator& lhs, const adsVarData::iterator& rhs) noexcept { return !(lhs == rhs); }
    adsVarData::iterator& adsVarData::iterator::operator++() noexcept {
        if (info_->name.empty())
            ++index_;
        else
            ++info_;
        return *this;
    }
    adsVarData::iterator adsVarData::iterator::operator++(int) noexcept { auto tmp = *this; ++*this; return tmp; }
    adsVarData::iterator::iterator(const adsData::subSymbolInfo* info, sizeType group, sizeType offset, sizeType index) noexcept
        : info_{ info }, group_{ group }, offset_{ offset }, index_{ index }
    {}
    

    Demo

    #include <algorithm>
    #include <iomanip>
    #include <iostream>
    #include <stdexcept>
    #include <string>
    #include <string_view>
    #include <utility>
    #include "adsData.h"
    
    using namespace std::literals::string_view_literals;
    
    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 GUID& guid) {
        return s << std::uppercase << std::hex << std::setfill('0')
            << std::setw(8) << guid.Data1 << '-'
            << std::setw(4) << guid.Data2 << '-'
            << std::setw(4) << guid.Data3 << '-'
            << std::setw(2) << static_cast<short>(guid.Data4[0])
            << static_cast<short>(guid.Data4[1])
            << '-' << static_cast<short>(guid.Data4[2])
            << static_cast<short>(guid.Data4[3])
            << static_cast<short>(guid.Data4[4])
            << static_cast<short>(guid.Data4[5])
            << static_cast<short>(guid.Data4[6])
            << static_cast<short>(guid.Data4[7])
            << std::nouppercase << std::dec;
    }
    
    std::ostream& operator<<(std::ostream& s, const adsVarData& var) {
        s   << "short: " << var.shortName() << '\n'
            << "type: " << var.type() << '\n'
            << "comment: " << var.comment() << '\n'
            << "typeId: " << var.typeId() << '\n'
            << "group: " << var.group() << '\n'
            << "offset: " << var.offset() << '\n'
            << "size: " << var.size() << '\n'
            << "is static: " << std::boolalpha << var.isStatic() << '\n';
        if (var.typeGuid())
            s << "type GUID: " << *var.typeGuid() << '\n';
        return s;
    }
    
    void showSubs(const adsVarData& var, std::string prefix) {
        for (auto&& sub : var) {
            std::cout << "name: " << sub.name(prefix) << '\n' << sub << '\n';
            showSubs(sub, sub.name(prefix));
        }
    }
    
    int main() {
        std::string varNames[] = {
            "MAIN.myInt",
        };
        try {
            adsData plcData{};
            for (auto&& var : plcData) {
                std::cout << var << '\n';
                showSubs(var, var.shortName());
            }
            for (auto& name : varNames)
                try {
                std::cout << plcData[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;
        }
    }
    

    gibt einfach alles aus.


  • Mod

    Ich glaube, es wäre sinnvoll, die längeren Schnipsel zu verlinken.



  • @camper

    Wahnsinn. Vielen Dank.

    Bevor ich da jetzt erst mal inhaltlich was dazu sagen kann muss ich mich erst mal eine Weile durchkämpfen.

    Frage: Wieso hast du jetzt keine _impl.h ->

    Für mich war zwar klar dass man dies macht um bestimmte Implemetierungsdetails zu verstecken. Aber das hast du ja auch mit dem namenlosen namespace gemacht. Und wann man nun was wo hin packt war nicht ganz so ersichtlich.



  • Den Code habe ich nun mal in mein Programm genommen.
    Kompiliert aber noch nicht folgender Fehler:

    Error	C1083	Cannot open include file: 'VersionHelpers.h': No such file or directory	c:\twincat\3.1\sdk\include\TcDef.h
    

  • Mod

    booster schrieb:

    Den Code habe ich nun mal in mein Programm genommen.
    Kompiliert aber noch nicht folgender Fehler:

    Error	C1083	Cannot open include file: 'VersionHelpers.h': No such file or directory	c:\twincat\3.1\sdk\include\TcDef.h
    

    verweist bei mir auf
    c:\Program Files\Windows Kits\10\Include\10.0.16299.0\um\VersionHelpers.h
    Evtl. musst du das Windows SDK noch mit dem Visual C++ Installer nachinstallieren.


  • Mod

    Neue Version mit ein paar zusätzlichen Kommentaren. Normaler C++ Code ab Zeile 760.



  • c:\Program Files\Windows Kits\10\Include\10.0.16299.0\um\VersionHelpers.h
    

    Evtl. musst du das Windows SDK noch mit dem Visual C++ Installer nachinstallieren

    Geht das auch bei Windows 7?

    Der Code ist jetzt etwas übersichtlicher 🙂
    Aber nur etwas. Darf ich meine Meinung kund tun:
    Ich finde es einfach nur schrecklich die öffnende Klammer { ans ende zu stellen und nicht in eine eigene Zeile. Und die funktionen ohne Leerzeilen aneinander gereeiht. Das mag vieleicht kompakt sein aber übersichtlich und lesbar nicht.


  • Mod

    booster schrieb:

    c:\Program Files\Windows Kits\10\Include\10.0.16299.0\um\VersionHelpers.h
    

    Evtl. musst du das Windows SDK noch mit dem Visual C++ Installer nachinstallieren

    Geht das auch bei Windows 7?

    Wieso meinst du, das wäre nicht möglich? Wie erstelltst du sonst Programme für Windows? Ich finde es eher seltsam, dass das Problem überhaupt auftritt. Der Include-Name deutet ja eher auf eine Datei hin, die schon immer in allen SDK-Versionen existiert hat, und das Vorhandensein irgendeiner Version ist nun mal essentiell.
    Habe testweise mal Windows SDK deinstalliert (ist ja eh eine Wegwerf-VM): Effekt ist, das das Verzeichnis verschwindet und der Compiler gleich mit einem fatalen Fehler aussteigt. Reinstallation per VS-Installer (Einzelkomponenten/SDKs, Bibliotheken und Frameworks) und alles funktioniert wieder.

    booster schrieb:

    Darf ich meine Meinung kund tun:
    Ich finde es einfach nur schrecklich die öffnende Klammer { ans ende zu stellen und nicht in eine eigene Zeile. Und die funktionen ohne Leerzeilen aneinander gereeiht. Das mag vieleicht kompakt sein aber übersichtlich und lesbar nicht.

    Darfst du, allerdings werde ich darauf nicht eingehen.
    Wir wissen alle, wie solche Diskussionen enden.
    Oder, wenn ich es mir recht überlege: nur komische Leute (Windowsprogrammierer) haben vertikal zuviel Platz 🙂



  • Darfst du, allerdings werde ich darauf nicht eingehen.
    Wir wissen alle, wie solche Diskussionen enden.
    Oder, wenn ich es mir recht überlege: nur komische Leute (Windowsprogrammierer) haben vertikal zuviel Platz 🙂

    😃 Schon klar dass du darauf nicht eingehst. War auch nicht meine Absicht.
    Und ich weiß nur die anderen komischen Leute (Linux Programmierer) haben horizontal zu viel Platz. Die würden am liebsten das ganze Programm in einer Zeile programmieren.

    Zurück zum Thema:

    Wieso meinst du, das wäre nicht möglich? Wie erstelltst du sonst Programme für Windows? Ich finde es eher seltsam, dass das Problem überhaupt auftritt.

    Ich meine natürlich das Windows 10 SDK...

    Ich habe folgendes Plattformtoolset eingestellt:
    Visual Studio 2017 - Windows XP (v141_xp)
    Weil wir noch XP Rechner unterstützen müssen.

    Als Windows SDK Version wird dann 7.0 ausgewählt. Dachte vielleicht ist die VersionHelpers.h da noch nicht definiert. Internet sagt aber gibt es schon seit VS 2013.

    Ich habe jetzt mal den Installer von VS2017 nochmals angeworfen. Dort kann man nur verschiedene Windows SDK in der Version 10 und 8.1 auswählen.
    Die sind aber installiert.


  • Mod

    In VersionHelpers.h scheint nichts für uns relevantes zu sein.
    Habe zum Test mal meine Version entfernt und einfach einen leeren Header VersionHelpers.h zum Projekt hinzugefügt, Projektverzeichnis zu zusätzlichen Includepfaden hinzugefügt (weil #include per <>), et voilà - kompiliert wieder. Das wäre also auch eine Möglichkeit.



  • - Die VersionHelpers.h habe ich nun zum Test einfach mal auskommentiert. Wie du sagst die braucht man nicht. Muss es dann auch so machen wie du sagtest.

    - Zu der Formatierung: Es gibt ja Resharper für c++. Der formatiert mir den Code auf Knopfdruck 😉

    - Hatte dich noch gefragt wieso du diesesmal die _impl.h weg gelassen hast. Und wo der Unterschied ist, den Code in eine _impl.h zu packen oder in die cpp in den unbenannten Namespace?

    - Was mich auch noch interessiert ist wieso die Ads.h und die TcAdsDef und TcAdsAPI.h viele doppelte definitionen beinhaltet.

    Da habe ich jetzt mal bei Beckhoff nachgefragt. Die Ordnerstruktur in der TwinCat 3.1 Installation ist auch noch mehr als verwirrend.



  • und noch ein Kompilierungsfehler:

    if (s[0] < '0' || s[0] > '9' || (std::numeric_limits<sizeType>::max() - (s[0] - '0')) / 10 < result)
    

    das max kennt er nicht;

    '(': illegal token on right side of '::'


  • Mod

    booster schrieb:

    und noch ein Kompilierungsfehler:

    if (s[0] < '0' || s[0] > '9' || (std::numeric_limits<sizeType>::max() - (s[0] - '0')) / 10 < result)
    

    das max kennt er nicht;

    '(': illegal token on right side of '::'

    unbedingt NOMINMAX in stdafx.h (wenn du hast) vor allen anderen Headern definieren.
    <windows.h> und Konsorten haben die Angewohnheit, sonst Macros für min und max zu definieren.



  • Hi camper

    unbedingt NOMINMAX in stdafx.h

    Klar. Ich hatte das NOMINMAX immer in den Projekteinstellungen drin. Ausgerechnet in diesem habe ich es nun vergessen. 👍

    Jetzt kennt er die Konstante ADSSYMBOLFLAG_STATIC nicht in:

    ref_property<symbol_entry_prop::isStatic, flag<
       AdsSymbolEntry, symbol_entry_prop::flags, ADSSYMBOLFLAG_STATIC>>,
    

    Die darüber liegenden sind ja alle in Ads.h drin. ADSSYMBOLFLAG_STATIC aber nicht.


  • Mod

    booster schrieb:

    Jetzt kennt er die Konstante ADSSYMBOLFLAG_STATIC nicht in:

    ref_property<symbol_entry_prop::isStatic, flag<
       AdsSymbolEntry, symbol_entry_prop::flags, ADSSYMBOLFLAG_STATIC>>,
    

    Die darüber liegenden sind ja alle in Ads.h drin. ADSSYMBOLFLAG_STATIC aber nicht.

    Dann kann ich nur vermuten, dass das neu und deine twincat Version zu alt ist. Bei mir ist sie drin (Dateidatum 22.02.2018 10:07 Uhr). Ich denke mir das ja nicht aus 😉

    Neue Version liest auch Attribute und Enums aus:
    adsData.h
    adsData.cpp
    main.cppund verzichtet praktisch gänzlich auf Zeigergefrickel.

    bzgl. _impl.h vs. Namespace - verstehe ich die Frage nicht so recht.
    Die _impl.h existierte, weil in diesem Code Teile des öffentlichen Headers private Typen benutzten, die dort schon vollständig definiert sein mussten. Das ist jetzt nicht mehr der Fall.



  • Dann kann ich nur vermuten, dass das neu und deine twincat Version zu alt ist. Bei mir ist sie drin (Dateidatum 22.02.2018 10:07 Uhr). Ich denke mir das ja nicht aus 😉

    Meine Ads.h ist vom 24.05.2017. Versteht wer will. Ich habe mir die neuste TwinCat Version letze Woche von der Hompage gezogen und installiert.


  • Mod

    Version? TC31-Full-Setup.3.1.4020.39.zip bei mir



  • Na dann ist meine neuer: 3.1.4022.16
    ??


  • Mod

    nvm. sehe gerade, dass ich auch Build 4022 habe, hatte die Datei nur verschoben.
    Evtl. funktionieren ja inplace Updates nicht so gut - also mal mit Deinstallation (schauen, dass die Verzeichnisse auch verschwinden) und dann Neuinstallation versuchen?


Anmelden zum Antworten