[Designfrage]OOP-Interpreter



  • Hi Leute!

    Seit geraumer zeit versuche ich ein für mich zufriedenstellende Klassenhirarchie für einen Interpreter zu erstellen.
    Was mir Schwierigkeit bereitet ist dabei die Implementierung des Typsystems, was vererbung unterstützen sollte.
    Habt ihr ein Paar Anregungen dazu?

    Ich dachte ich nehme eine Baumstruktur um die Bezeichner/Eigenschafts beziehung abzubilden. Ein Objekt wäre dann eine ID die dann ein Objekt im Baum Representiert, und ein Typ lediglich eine "Objekt-Schablone" die bei instanzierung einfach kopiert wird...
    Naja das war meine erste Idee, aber da fehlen die vererbungsbeziehungen und es gibt kein Typsystem...

    Was sind den die geläufigen Ideen?



  • Was für ein Interpreter ist das? Kannst du dein Vorhaben genauer beschreiben? Hast du schon experimentiert?

    Ohne die Antworten auf diese Fragen zu kennen ein kleiner Entwurf von mir. Was entspricht daran deinen Vorstellungen und was nicht?

    #include <vector>
    #include <string>
    #include <iostream>
    #include <boost/any.hpp>
    #include <boost/foreach.hpp>
    
    struct Type
    {
    	virtual ~Type()
    	{
    	}
    
    	virtual size_t size() const = 0;
    	virtual void constructDefault(void *dest) const = 0;
    	virtual void construct(void *dest, const boost::any &value) const = 0;
    };
    
    template <class T>
    struct GenericType : Type
    {
    	virtual size_t size() const
    	{
    		return sizeof(T);
    	}
    
    	virtual void constructDefault(void *dest) const
    	{
    		new (dest) T();
    	}
    
    	virtual void construct(void *dest, const boost::any &value) const
    	{
    		new (dest) T(boost::any_cast<T>(value));
    	}
    };
    
    struct MemberVariable
    {
    	const Type *type;
    	std::string name;
    	boost::any defaultValue;
    
    	MemberVariable(const Type &type, std::string name, boost::any defaultValue = boost::any())
    		: type(&type)
    		, name(name)
    		, defaultValue(defaultValue)
    	{
    	}
    };
    
    struct Class
    {
    	std::vector<MemberVariable> members;
    
    	virtual void construct(std::vector<char> &memory) const
    	{
    		initializeMembers(memory);
    	}
    
    	virtual bool findMember(std::string name, size_t &offset) const
    	{
    		BOOST_FOREACH (const MemberVariable &mem, members)
    		{
    			if (mem.name == name)
    			{
    				return true;
    			}
    
    			offset += mem.type->size();
    		}
    
    		return false;
    	}
    
    private:
    
    	void initializeMembers(std::vector<char> &memory) const
    	{
    		BOOST_FOREACH (const MemberVariable &mem, members)
    		{
    			size_t oldSize = memory.size();
    			memory.resize(oldSize + mem.type->size());
    			void *member = &memory[0] + oldSize;
    			if (mem.defaultValue.empty())
    			{
    				mem.type->constructDefault(member);
    			}
    			else
    			{
    				mem.type->construct(member, mem.defaultValue);
    			}
    		}
    	}
    };
    
    struct DerivedClass : Class
    {
    	Class *parent;
    
    	virtual void construct(std::vector<char> &memory) const
    	{
    		parent->construct(memory);
    		Class::construct(memory);
    	}
    
    	virtual bool findMember(std::string name, size_t &offset) const
    	{
    		return
    			parent->findMember(name, offset) ||
    			Class::findMember(name, offset);
    	}
    };
    
    struct Object
    {
    	const Class &type;
    	std::vector<char> elements;
    
    	Object(const Class &type)
    		: type(type)
    	{
    		type.construct(elements);
    	}
    };
    
    template <class T>
    T *getElement(Object &obj, std::string name)
    {
    	size_t offset = 0;
    	if (!obj.type.findMember(name, offset))
    		return 0;
    
    	return reinterpret_cast<T *>(&obj.elements[0] + offset);
    }
    
    int main()
    {
    	GenericType<int> integer;
    	GenericType<Object *> pointer;
    
    	Class a;
    	a.members.push_back(MemberVariable(integer, "count", boost::any(123)));
    
    	DerivedClass b;
    	b.parent = &a;
    	b.members.push_back(MemberVariable(pointer, "other"));
    
    	Object instanceA(a);
    	Object instanceB(b);
    
    	using namespace std;
    
    	cout << *getElement<int>(instanceA, "count") << endl;
    	cout << *getElement<int>(instanceB, "count") << endl;
    	cout << *getElement<Object *>(instanceB, "other") << endl;
    }
    


  • InterPie schrieb:

    Was sind den die geläufigen Ideen?

    Machs simpel und repräsentiere jedes Objekt so:

    struct object {
      std::map<std::string, std::unique_ptr<object> > member;
      std::map<std::string, function> methods;
    };
    

    Perl und Python machen das ähnlich.

    Wenn A von B erbt, kopierst du einfach alle Daten und Funktionen von A nach B, sofern sie nicht von B definiert wurden.

    Wenn man dynamische Sprachen hat, was ich jetzt als vorausgesetzt betrachtete, braucht das Objekt seine Klassenhirarchie nicht zu kennen. Es ist eher mit C++-Templates vergleichbar, die zur Laufzeit ausgewertet werden (und nicht so ein Mischling wie der Code TyRoXx).


Anmelden zum Antworten