vendredi 6 septembre 2013

Callbacks on C++ classes methods

My problem of the day : how to designate a NON STATIC method of any class as a callback (i.e a method triggered by any event), without using Boost and others craps ?

Fortunately I was able to draw inspiration from this page (http://www.partow.net/programming/templatecallback) that offers an interesting template.

Only one problem left: to instantiate the callback object the client must still designate in the template argument type / name the class hosting the method that we wishes to designate as a callback, wich is quite annoying...


Therefore, I propose the following mod, involving inheritance on a callback interface class (BaseCallback) :

template <typename ReturnType, typename Parameter>
class BaseCallback
{
public:

    virtual ReturnType operator()(Parameter parameter) = 0;
    virtual ReturnType execute(Parameter parameter) = 0;
};


template <class Class, typename ReturnType, typename Parameter>
class CallBack : public BaseCallback<ReturnType, Parameter>
{
public:

   typedef ReturnType (Class::*Method)(Parameter);

   CallBack(Class* class_instance, Method method)
   : class_instance_(class_instance),
     method_(method)
   {}

   virtual ReturnType operator()(Parameter parameter)
   {
      return (class_instance_->*method_)(parameter);
   }

   virtual ReturnType execute(Parameter parameter)
   {
      return operator()(parameter);
   }

private:

   Class* class_instance_;
   Method method_;
};


We have now a total decoupling between owner class callback method (client) and the calling class, it does not need to know the definition of the client class :-)


Here is a little example:

class Handler
{
protected:
    CallBack<Handler, void, const std::string&>* m_cb;

public:

    Handler( void )
    {
        m_cb = new CallBack<Handler, void, const std::string&>( this, &Handler::Func );
    }


    void Func( const std::string& a )
    {
        printf( "It works : %s\n", a.c_str() );
    }

    BaseCallback<void, const std::string&>* GetCallback( void )
    {
        return m_cb;
    }

};


class EvtSource
{
protected:

    BaseCallback<void, const std::string&>* m_handler;

public:
    
    
    void RegisterHandler( BaseCallback<void, const std::string&>* p_handler )
    {
        m_handler = p_handler;
    }
    
    void DoSomething( void )
    {
        (*m_handler)( "test01" );
    }
};


int main(int argc, char *argv[])
{
    Handler e;
    
    EvtSource foo;
    foo.RegisterHandler( e.GetCallback() );

    foo.DoSomething();
}

Enjoy ! ;-)

lundi 19 août 2013

Short post with technical news. I continuously work on improving my 3D engine. About multithread management, I found very an interesting page, and my current work is strongly based on it : http://accu.org/index.php/journals/562


jeudi 6 juin 2013

C++ plugin framework

In this post I'll expose a little C++ framework to manage a plugin loading system. I wrote this one some years ago for one of my IRL job projects, and now I'm using it in my 3D engine.

1/ Defining the plugin interface


plugin.h :

     class IRenderingPlugin
   {
       virtual long Init( HWND* p_hwnd ) = 0;  // init graphic engine and pass the window handle
    

       virtual void Release( void ) = 0;      // release graphic engine (unallocate all resources, ...)

       virtual void Draw2DSprite( int posx, int posy, char* image ) = 0; // draw something in 2D ...
   };


2/ Plugin side : creating an openGL plugin

the plugin specific code implement the previously defined interface:

OpenGLPlugin.h :

    #include "plugin.h"

    class OpenGLRenderingPlugin : public IRenderingPlugin
    {
       long Init( HWND* p_hwnd );
       void Release( void );

       void Draw2DSprite( int posx, int posy, char* image );
    }


OpenGLPlugin.cpp :

      #include "OpenGLPlugin.h"

      long Init( HWND* p_hwnd )
      {
            // put here OpenGL init stuff code

            return 0; // everything is OK
      }

      void Release( void )
      {
            // put here OpenGL stopping stuff code
      }


      void Draw2DSprite( int posx, int posy, char* image )
      {
            // put here specific openGL code to draw a 2D sprite on screen
      }
The final step to create our lib plugin is to export a simple function that instanciate our specific interface implementation and return a pointer on that:

on linux platform:

      #include "OpenGLPlugin.h"

      extern "C"
      {
      IRenderingPlugin* PIFactory( void )
      {
            return new OpenGLRenderingPlugin;
      }
      }

on windows platform:

       #include "OpenGLPlugin.h"

      extern "C"
      {
      __declspec(dllexport) IRenderingPlugin* PIFactory( void )
      {
            return new OpenGLRenderingPlugin;
      }
      }


If you work under MSVC, the "extern "C"" statement disable microsoft specific functions names decoration, making your DLL plugin compliant even for binaries generated with other compilers than MSVC.

On Windows, While opening your DLL binary with the tool 'Dependency walker' you can see that your plugin export only one function : PIFactory()



3/ Main program side : the plugin manager, heart of the system

The plugin manager provides method to load a plugin (LoadPlugin()), and a method to find and execute the DLL exported C function "PIFactory()" (Instanciate()). 


The LoadPlugin() method call specific API functions to load a dynamic library (.so on linux, .dll on win32) : dlopen() for linux, LoadLibraryA for windows. The Instanciate() method locate and execute the PIFactory() method exported by our plugin binary to instanciate the plugin interface specific implementation...To locate an exported function, on windows we use GetProcAddress(); on Linux we use dlsym().



Notice that the plugin manager is a template, because we don't want it to be linked with a specific interface definition. Finally, notice that this is implemented as a singleton (no need for multiple instances of plugin manager, only one is enough).


The following code is compliant for both windows and linux platforms.


pimanager.h :

      #ifndef _PIMANAGER_H_
      #define _PIMANAGER_H_


      #ifdef WIN32
      #include <windows.h>
      #else
      #include <dlfcn.h>
      #endif

      #include <string>
      #include <map>

      typedef enum
      {
        PIM_OK,
        PIM_OK_PIALREADYLOADED,
        PIM_FAIL_PILOADING,
        PIM_FAIL_PIUNLOADING,
        PIM_FAIL_UNKNOWN,
        PIM_FAIL_FACTORYFUNCNOTFOUND,
      } PluginManagerStatus;

      #define PIFACTORYSYMBOLNAME "PIFactory"

      template <typename base>
      class CPlugInManager
      {
      public:

      #ifdef WIN32
        typedef HMODULE Handle;
      #else
        typedef void*   Handle;
      #endif


      private:

        typedef struct
        {
          Handle        handle;
          std::string   path;
          long          refcount;
        } PluginInfos;

        typedef base* (* Factory)( void );

        typedef std::map<std::string, PluginInfos> LibList;

        CPlugInManager( void )  { };
        ~CPlugInManager( void ) { };

        static LibList* get_lib_list()
        {
          static LibList m_libs;
          return &m_libs;
        }

      public:
        static PluginManagerStatus LoadPlugin( const char* p_path, Handle& p_handle );
        static PluginManagerStatus UnloadPlugin( const char* p_path );
        static PluginManagerStatus Instanciate( Handle p_handle, base** p_inst );
      };

      #include "PIManager_impl.h"

      #endif

PIManager_impl.h:

      template <typename base>
      PluginManagerStatus CPlugInManager<base>::LoadPlugin( const char* p_path, Handle& p_handle )
      {

        typename LibList::iterator it = get_lib_list()->find( p_path );
        if( it == get_lib_list()->end() )
        {
      #ifdef WIN32
          HMODULE hMod = LoadLibraryA( p_path );
      #else
          void* hMod = dlopen( p_path, RTLD_LAZY );
      #endif
          if( hMod == NULL )
          {
            return PIM_FAIL_PILOADING;
          }
          else
          {
            PluginInfos pii;
            pii.refcount = 1;
            pii.handle = hMod;
            pii.path   = p_path;
            LibList* ll = get_lib_list();
            (*ll)[p_path] = pii;
            p_handle   = hMod;
            return PIM_OK;
          }
        }
        else
        {
          // plugin already loaded

          p_handle = it->second.handle;
          it->second.refcount++;

          return PIM_OK_PIALREADYLOADED;
        }

       
        return PIM_OK;
      }

      template <typename base>
      PluginManagerStatus CPlugInManager<base>::UnloadPlugin( const char* p_path )
      {

          typename LibList::iterator it = get_lib_list()->find( p_path );
          if( it == get_lib_list()->end() )
          {
            return PIM_FAIL_UNKNOWN;
          }

          else
          {
            it->second.refcount--;       
            if( it->second.refcount == 0 ) // si plus aucuns hub ne fait reference a ce plugin
            {
                PluginInfos pii = it->second;
                get_lib_list()->erase( it );
      #ifdef WIN32
                FreeLibrary( pii.handle );
      #else
                dlclose( pii.handle );
      #endif
            }
          }
            return PIM_OK;
      }

      template <typename base>
      PluginManagerStatus CPlugInManager<base>::Instanciate( Handle p_handle, base** p_inst )
      {
       
      #ifdef WIN32
        FARPROC proc = GetProcAddress( p_handle, PIFACTORYSYMBOLNAME );
      #else
        Factory proc = (Factory)dlsym( p_handle, PIFACTORYSYMBOLNAME );
      #endif

        if( proc == NULL )
        {
          return PIM_FAIL_FACTORYFUNCNOTFOUND;
        }
        else
        {
          Factory factory = (Factory)proc;
          base* inst = (*factory)();
          *p_inst = inst;
          return PIM_OK;
        }
       
        return PIM_OK;
      }

4/ How to use the plugin manager in your main program

To load a plugin and retrieve a pointer to the implemented interface proceed like that :

     // plugin handle declaration
      CPlugInManager<IRenderingPlugin>::Handle pihandle;

      // interface pointer
      IRenderingPlugin* renderer;

      PluginManagerStatus pistatus = CPlugInManager<IRenderingPlugin>::LoadPlugin( "openglpi.dll", pihandle );
      if( pistatus != PIM_OK )
      {
            // something went wrong : cannot find the file, file existe but cannot find the exported func,
            return IMFALSE;
      }

      // now we can instanciate the plugin specific interface implementation:
      if( CPlugInManager<IRenderingPlugin>::Instanciate( pihandle, &renderer ) != PIM_OK )
      {
            return IMFALSE;
      }


After this init phase, you can now call your plugin functions and do the work !

      renderer->Init( wnd ); // graphic middleware init (openGL here)

In your rendering loop (window repaint) :

      renderer->Draw2DSprite( 60, 100, sprite_img );


Enjoy ! ;)

jeudi 28 mars 2013

OOP in Lua

I am currently working on giving total control of the game engine through Lua scripting : more infos here : http://spacesimcentral.com/ssc/topic/3508-sgvm-and-various-tools/

This mean that Lua code will be a little bit more complex. Knowing that Lua is not an object-orient langage, I decided to use the Luna wrapper : this is a cool C++ template that provide an ersatz of OOP extension to the lua langage.

Luna Wrapper template is available here : http://lua-users.org/wiki/LunaWrapper

samedi 9 février 2013

samedi 5 janvier 2013

Foggy clouds

Today I made a small modification in the volumetric clouds shader, by adding a fog effect; here are some screens comparing clouds rendering with and without fog effect:



Without fog effect : 



With fog effect : 





This tiny fog effect on clouds should also be useful for giant gas atmosphere rendering, but we'll see that point later ;)