[Solved] operator[] - beliebige Typen Lesen/Schreiben



  • Angenommen ich habe folgende Klassen:

    class BaseProperty {
    public:
    	using Ptr = std::unique_ptr<BaseProperty>;
    	virtual ~BaseProperty() { }
    };
    
    template<typename T>
    class Property : public BaseProperty {
    public:
    	Property(const std::string& name, const T& value)
    		:BaseProperty{}, val{ value }
    	{
    	}
    
    	const T& get() const { return val; }
    	void set(const T& newVal) { val = newVal; }
    
    private:
    	T val;
    };
    
    class PropertyHolder {
    public:
        // ...
    private
        std::map<std::string, BaseProperty::Ptr> props;
    };
    

    Kann man mit relativ wenig Aufwand (<50 Zeilen) erreichen, dass ich die Klasse folgendermaßen verweden kann, und wie heißt das was ich da mache? (Das macht sol2 (LuaWrapper) so, aber ich werd aus dem Code nicht schlau)

    PropertyHolder h;
    h["HP"] = 25;
    h["name"] = "Max Mustermann";
    int hp = h["HP"]
    // v.s.
    h.set<int>("HP", 25);
    h.set<std::string>("name", "Max Mustermann");
    int hp = h.get<int>("HP");
    

    Oder bedarf es dessen irgendwelche Magie, für die mein Lvl noch zu gering ist?

    Ich würde mich über ein paar Tips sehr freuen.

    LG


  • Mod

    ungetestet (kann Schreibfehler enthalten) z.B. so

    class PropertyProxy {
    	PropertyProxy(BaseProperty::Ptr& p, const std::string& name) : p(p) {}
    	template <typename T>
    	PropertyProxy operator=(const T& v) const {
    		p.reset( new Property<T>( name, v );
    		return *this;
    	}
    	const PropertyProxy& operator=(const char* str) const {
    		p.reset( new Property<std::string>( name, str );
    		return *this;
    	}
    	template <typename T>
    	operator const T&() const {
    		auto p = dynamic_cast<Property<T>*>( p.get() );
    		return p ? p->get() : throw std::bad_cast();
    	}
    	BaseProperty::ptr& p;
    	std::string name;
    }
    
    class PropertyHolder {
    	PropertyProxy operator[](const std::string& name) {
    		return { props[name], name };
    	}
    private:
    	std::map<std::string, BaseProperty::ptr> props;
    }
    

    (wozu name in Property mitgeschleppt wird, ist mir nicht ganz klar - logischer wäre es evtl. als Member von BaseProperty und ein std::set in PropertyHolder).



  • Hallo camper,

    vielen Dank für Deine Antwort!

    Ich muss sagen, ich habe das mit dem Namen etwas vermasselt, hatte mich kurzfristig entschlossen den Namen nicht zu speichern, weil ich diesen ja als Key für die Map verwende. Dann habe ich aber vergessen es in der ctor Parameterliste zu entfernen, entschuldige falls dies für Verwirrung sorgte!

    Das ist mehr als Ausreichend, damit ich das Grundprinzip verstehen kann, scheint auch gut zu funktionieren (Ich gekomme noch eine Warnung wegen dem return von p->get() weil p ein temporärer Wert ist, aber das worauf p zeigt müsste ja gültig sein). Hat mir echt weitergeholfen.

    Ich habe noch den Code aus sol2 gefunden, mit dem ich nicht so viel anfangen konnte, ich meine aber es hat einen ähnlichen Zweck:

    struct proxy_base_tag {};
    
    	template <typename Super>
    	struct proxy_base : proxy_base_tag {
    		operator std::string() const {
    			const Super& super = *static_cast<const Super*>(static_cast<const void*>(this));
    			return super.template get<std::string>();
    		}
    
    		template<typename T, meta::enable<meta::neg<meta::is_string_constructible<T>>, is_proxy_primitive<meta::unqualified_t<T>>> = meta::enabler>
    		operator T () const {
    			const Super& super = *static_cast<const Super*>(static_cast<const void*>(this));
    			return super.template get<T>();
    		}
    
    		template<typename T, meta::enable<meta::neg<meta::is_string_constructible<T>>, meta::neg<is_proxy_primitive<meta::unqualified_t<T>>>> = meta::enabler>
    		operator T& () const {
    			const Super& super = *static_cast<const Super*>(static_cast<const void*>(this));
    			return super.template get<T&>();
    		}
    	};
    

    EDIT:
    Ich verwende VS2017 und bei mir hatte es immer geknallt, wenn ich einen string gespeichert und dann ausgelesen hab.

    // Ausgelöste Ausnahme
    // Ausnahme ausgelöst bei 0x5518465E (vcruntime140d.dll)
    // 0xC0000005: Zugriffsverletzung beim Lesen an Position 0x00EA4000.
    
    // iosfwd
    template<> struct char_traits<char>
    	{	// properties of a string or stream char element
    // ...
    	static _Elem * __CLRCALL_OR_CDECL copy(_Out_writes_(_Count) _Elem * const _First1,
    		_In_reads_(_Count) const _Elem * const _First2, const size_t _Count) _NOEXCEPT // strengthened
    		{	// copy [_First2, _First2 + _Count) to [_First1, ...)
    		return ((_Elem *)_CSTD memcpy(_First1, _First2, _Count));  // <---
    		}
    
    PropertyMap h;
    h["HP"] = "Hello";
    std::string t = h["HP"]; // outsch
    std::cout << t << '\n';
    

    Es wurde keine Exception abgefangen, obwohl ich sogar catch(...) am Ende der main habe 😕 und ich war fast am Verzweifeln. Tjap Sachen gibts, hab glatt die Funktion in (m. E. nach) etwas Equivalentes umgeschrieben und prompt funktioniert es:

    template<typename T>
    operator const T&() const 
    {
        if (auto p = dynamic_cast<Value<T>*>(ptr.get()))
    	    return p->val;
        throw std::bad_cast{};
        //auto p = dynamic_cast<Value<T>*>(ptr.get());
        //return p ? p->val : throw std::bad_cast{};
    }
    

    Dazu sag ich nur 😮 blick ich nicht.


Anmelden zum Antworten