views:

52

answers:

2

Hello all,

I am working on a AI dynamic link library. It is to be explicitly linked and all of this is handled by a simple header include.

I am at the moment trying to create the code for the DLL to be able to call functions in the main EXE to manipulate the world and also to be able to query functions to learn about the state of the world. I am now at the point where I can call void return functions (be they global functions or member functions) with no parameters.

I am now trying to implement the ability to call functions in the EXE and get the return value from them. (non-void functions) Things are not going well.. I've been trying for a while now trying to find the best way to implement this. I can use boost libraries in the DLL but not in the EXE.

I'm going to do a dump of the relavent code here. I know it is alot but I hope someone will be able to point out how I can improve.

Even if you do not read the code (very understandable) it would be very helpful to know how you would go about tackling this problem in general terms.

Here goes (I've tried to cut out as many unrelated sections of code as I can):

---------------------------------------EXE side header--------------------------------------

typedef void (*Command)();
typedef void (*CommandMF)(int, std::string);

typedef void (*AddEntityFunction)(int&);
typedef void (*AddActionToEntityFunction)(int, Command);
typedef void (*AddActionToEntityFunctionMF)(int, CommandMF, std::string);
typedef void (*UpdateDoubleThinkFunction)();


class MemberFunctionStorageExecute
{
    public:
        virtual void Execute() const =0;
    };


template <class T>
struct MemberFunctionStorage : MemberFunctionStorageExecute
{
    typedef void (T::*MemberFunctionWorker)();

        MemberFunctionWorker mbw;
    T *obj;

    virtual void Execute() const
{
    (obj->*mbw)();
}
};

typedef std::map<std::string, MemberFunctionStorageExecute*> MemberFunctionsList;
typedef std::map<int, MemberFunctionsList*> ListofLists;


//Template hack to allow static properties inside header
template <class T>
class DoubleThinkInterfaceImpl
{
protected: 
    static HINSTANCE hinstDLL;
    static AddEntityFunction DLLAddEntity;
    static AddActionToEntityFunction DLLAddActionToEntity;
    static AddActionToEntityFunctionMF DLLAddActionToEntityMF;
    static UpdateDoubleThinkFunction DLLUpdateDoubleThink;

    static ListofLists m_plistlist;
};
template <class T>
HINSTANCE DoubleThinkInterfaceImpl<T>::hinstDLL;
template <class T>
AddEntityFunction DoubleThinkInterfaceImpl<T>::DLLAddEntity;
template <class T>
UpdateDoubleThinkFunction DoubleThinkInterfaceImpl<T>::DLLUpdateDoubleThink;
template <class T>
AddActionToEntityFunction DoubleThinkInterfaceImpl<T>::DLLAddActionToEntity;
template <class T>
AddActionToEntityFunctionMF DoubleThinkInterfaceImpl<T>::DLLAddActionToEntityMF;
template <class T>
ListofLists DoubleThinkInterfaceImpl<T>::m_plistlist;



class DoubleThinkInterface : protected DoubleThinkInterfaceImpl<int>
{
private:
    int m_pid;
    MemberFunctionsList m_pmemfunlist;


public:
    int ID()
    {
        return m_pid;
    }

    DoubleThinkInterface()
    {
        if(!hinstDLL)
        {
            hinstDLL = LoadLibrary("DoubleThink.dll");
            DLLAddEntity = (AddEntityFunction)GetProcAddress(hinstDLL, "AddEntity");
            DLLUpdateDoubleThink = (UpdateDoubleThinkFunction)GetProcAddress(hinstDLL, "Update");
            DLLAddActionToEntity = (AddActionToEntityFunction)GetProcAddress(hinstDLL, "AddActionToEntity");
            DLLAddActionToEntityMF = (AddActionToEntityFunctionMF)GetProcAddress(hinstDLL, "AddActionToEntityMF");
        }
        DLLAddEntity(m_pid);
        DoubleThinkInterface::m_plistlist.insert(std::pair<int, MemberFunctionsList*>(m_pid, &m_pmemfunlist));
    }

    ~DoubleThinkInterface()
    {
        //if(hinstDLL != 0)
        //  FreeLibrary(hinstDLL);
    }

    void AddAction(Command action)
    {
        DLLAddActionToEntity(m_pid, action);
    }

    void Update()
    {
        DLLUpdateDoubleThink();
    }

    template <class T>
    void AddActionMF(T *object, void (T::*memberfunc)(), std::string actionName)
    {
        MemberFunctionStorage<T> *store = new MemberFunctionStorage<T>;
        store->mbw = memberfunc;
        store->obj = object;
        m_pmemfunlist.insert(std::pair<std::string, MemberFunctionStorageExecute*>(actionName, store));
        DLLAddActionToEntityMF(m_pid, &DoubleThinkInterface::ResolveMF, actionName);
    }

    static void ResolveMF(int idnum,std::string mfName)
    {
        ListofLists::iterator lit;
        lit = m_plistlist.find(idnum);
        MemberFunctionsList::iterator it;
        it = lit->second->find(mfName);
        it->second->Execute();
    }
};

-------------------------------EXE-side example------------------------------------

class BaseEntity
{
public:
    DoubleThinkInterface dtInterface;
    BaseEntity(){}
    virtual ~BaseEntity(){}
};

class Humanoid : public BaseEntity
{
public:
    Humanoid(){}
    ~Humanoid(){}

    std::string name;

    void Move();

    int GetAge(){return 10;}
};

void Humanoid::Move()
{
    std::cout << name << ": I'm moving around and such \n";
}

void EndLifeAsWeKnowIt()
{
    cout << "Suddenly everything changed... \n";
}

int _tmain(int argc, _TCHAR* argv[])
{
    Humanoid *entity = new Humanoid();
    entity->name = "Bobby";
    entity->dtInterface.AddAction(&EndLifeAsWeKnowIt);
    entity->dtInterface.AddActionMF<Humanoid>(entity, &Humanoid::Move, "Move");

    entity->dtInterface.Update();

    int x; cin >> x;

    return 0;
}

-------------------------DLL-side code------------------------------------

DTEntityManager* DTEntityManager::Instance()
{
    static DTEntityManager instance;

    return &instance;
}

template<class T>
void AddAction(int id, void (*comm)(int, std::string), std::string mfid)
{
    DTEntity *ent = DTEntityManager::Instance()->GetEntityFromID(id);

    CommandMemberFunction<T> *newcomm = new CommandMemberFunction<T>();
    newcomm->comm = comm;
    newcomm->entityid = id;
    newcomm->mfid = mfid;

    ent->SetCommandMF(newcomm);
}

extern "C"
{
    DLL_EXPORT void AddEntity(int &idnumber)
    {
        DTEntity *entity = new DTEntity();
        idnumber = entity->ID();
        DTEntityManager::Instance()->RegisterEntity(entity);
    }

    DLL_EXPORT void AddActionToEntity(int id, void (*comm)())
    {
        DTEntity *ent = DTEntityManager::Instance()->GetEntityFromID(id);

        CommandGlobal<void> *newcomm = new CommandGlobal<void>();
        newcomm->comm = comm;

        ent->SetCommand(newcomm);
    }

    DLL_EXPORT void AddActionToEntityMF(int id, void (*comm)(int, std::string), std::string mfid)
    {
        AddAction<void>(id, comm, mfid);
    }

    DLL_EXPORT void AddActionToEntityMF_int(int id, void (*comm)(int, std::string), std::string mfid)
    {
        AddAction<int>(id, comm, mfid);
    }
}

--------------------------DLL-side Structure for holding callbacks ---------------------------

class CommandBase
{
public:
    virtual void Execute() const =0;
};

template<class T>
struct CommandGlobal : CommandBase
{
    typedef boost::function<T ()> Command;
    Command comm;

    virtual T Execute() const
    {
        return comm();
    }
};

template<class T>
struct CommandMemberFunction : CommandBase
{
    typedef boost::function<T (int, std::string)> Command;
    Command comm;
    int entityid;
    std::string mfid;

    virtual T Execute() const
    {
        return comm(entityid, mfid);
    }
}; 

At the moment the DLL doesn't compile because of this line:

AddAction<int>(id, comm, mfid);

Because it tries to override

virtual void Execute() const =0;

with a function which returns int. Gives non-covariance error.. I know I have been barking up the wrong tree but I can't see any other solution at the moment either.

Does anyone have any advice on how to do it better? Even if it just a vague direction I should direct my attention in I would appreciate it. Thanks a lot if you bother to read all this!

A: 

Quick & dirty answer: pass to Execute a reference to the result type as a void*, and make Execute private. Then wrap Execute in a non-virtual wrapper which returns T by value and does the cast.

Alexandre C.
Wouldn't that require making CommandBase a template? I would like the DTEntities to hold these structures (with differing Execute() return types) as a map of CommandBase*. Would this solution allow for that? Thanks!
Sebastian Edwards
If anyone could clarify Alexandre's idea with a little code I would be most grateful. Thanks all.
Sebastian Edwards
+1  A: 

I think you are complicating too much. See this:

/*************** both ***************/
typedef void (*Prototype1)();
typedef void (*Prototype2)(int);
typedef int (*Prototype3)();

struct FuncList {
    Prototype3 func1;
    Prototype1 func2;
    Prototype1 func3;
    Prototype2 func4;
    Prototype1 func5;
    // ...
};

/*************** dll ***************/

FuncList func_list;

Prototype3 &func1 = func_list.func1;
Prototype1 &func2 = func_list.func2;
Prototype1 &func3 = func_list.func3;
Prototype2 &func4 = func_list.func4;
Prototype1 &func5 = func_list.func5;

/* DLLEXPORT */ void SetFuncList(const FuncList &list) { func_list = list; }

void UsageExample()
{
    /* Just call the function */
    func2();
}

/*************** exe ***************/

/* declarations (functions must be defined somewhere) */
int func1();
void func2();
void func3();
void func4(int);
void func5();

const FuncList func_list = {
    func1,
    func2,
    func3,
    func4,
    func5
};

typedef void (*SetFuncListProc)(const FuncList &list);
SetFuncListProc SetFuncList;

void Init()
{
    /* ... load the DLL, load "SetFuncList" ... */

    SetFuncList(func_list);
}
adf88