• Advertisement
Sign in to follow this  

Unity C# Delegate in C++?

This topic is 3846 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm sure that this doesn't fully match the C# delegate spec as I did not look to closer at the spec. However, this method does allow for me to make an event handler interface that lets me connect event listeners and automatically disconnect them when they go out of scope in a thread safe manner. I was inspired to do this a GDNet thread, the realization that Boost signal is not thread safe, and the feeling that my previous solution could be improved upon. This is the prototype that I came up with. Do note that to ensure thread safety, the user must make all event listeners thread safe. Listeners can be called at any time.
template <typename ListenerT,typename TriggerFuncT>
class Delegate
{
public:
    typedef TriggerFuncT tiggerFunc_t; // trigger function type
    typedef boost::shared_ptr<ListenerT> listenerP_t; // function pointer type
    typedef boost::weak_ptr<ListenerT> listenerWkP_t; // weak function pointer type
    // connects a function pointer to the delegate
    inline void Connect(listenerP_t func) { boost::mutex::scoped_lock lock(mutex);functions.push_back(listenerWkP_t(func)); };

    // triggers all objects in functions vector
    void Trigger(TriggerFuncT &trigger)
    {
        boost::mutex::scoped_lock lock(mutex); // lock access mutex
        typedef typename std::vector<listenerWkP_t>::iterator it_t;
        listenerP_t p;
        for (it_t i=functions.begin();i!=functions.end();i++)
        {
            // copy to shared_ptr
            p=i->lock();
            // if not expired then trigger
            if (!i->expired())
                trigger(p);
            else // elsewise remove from functions list
                functions.erase(i);
        }
    }
private:
    boost::mutex mutex; // access mutex
    std::vector<listenerWkP_t> functions; // list of functions
};

// USAGE EXAMPLE

// EXAMPLE LISTENER BASE CLASS
class ResChangeListener
{
    public:
    virtual void operator()(const math::vector2i &resolution) = 0;
};

// EXAMPLE LISTENER TRIGGER
class ResChangeTrigger
{
public:
    // delegate type
    typedef Delegate<ResChangeListener,ResChangeTrigger> delegate_t;
    // function pointer type
    typedef delegate_t::listenerP_t listenerP_t;

    ResChangeTrigger(const math::vector2i &resolution_) : resolution(resolution_) {};
    void operator () (listenerP_t func)
    { func->operator()(resolution); };
private:
    math::vector2i resolution; // new resolution
};

// EXAMPLE USE OF A DELEGATE
class Window
{
public:
    // constructs window
    Window(const std::string &appTitle,math::vector2i &resolution,bool isFullscreen);
    // sets the window dimensions
    void SetResolution(const math::vector2i &resolution);
    // swaps the display buffers
    void SwapBuffers() const { glfwSwapBuffers(); };
    // issues request to close window
    void Quit() {};
    // set an on reschange listener
    void OnResChange(ResChangeTrigger::listenerP_t func) { resChangeEvent.Connect(func); };
private:
    ResChangeTrigger::delegate_t resChangeEvent;
    boost::mutex mutex; // thread mutex for window
    GLFWInstance glfwIns; // glfw Instance
};

// EXAMPLE RESCHANGE LISTENER OBJECT
class ResChanger : public renwin::ResChangeListener
{
    public:
    ResChanger(math::vector2i &resolution_) : resolution(resolution_),isChanged(false) {};
    void operator()(const math::vector2i &resolution_)
    {
        boost::mutex::scoped_lock lock(mutex);
        isChanged=true;
        resolution=resolution_;
    };
    bool IsChanged() { return isChanged; };
    math::vector2i Resolution() { boost::mutex::scoped_lock lock(mutex);return resolution; };
    private:
    volatile bool isChanged; // is changed flag
    math::vector2i resolution; // screen resolution
    boost::mutex mutex; // access mutex
};

// EXAMPLE IMPLEMENTATION CODE IN MAIN
math::vector2i screen(1024,768); // screen dimensions

renwin::ResChangeTrigger::listenerP_t func(new ResChanger(screen));
window.OnResChange(func);
window.SetResolution(math::vector2i(800,600));

log.Mode(lg::MESSAGE)<<screen; // logs the screen dimensions to file
if (((ResChanger*)func.get())->IsChanged())
    screen=((ResChanger*)func.get())->Resolution();
log.Mode(lg::MESSAGE)<<screen; // logs the screen dimensions to file

The log output shows success
Quote:
Log <Entry caller="main()" level="Message">[ 1024 768 ]</Entry> <Entry caller="main()" level="Message">[ 800 600 ]</Entry>
I am still experimenting with this code but I'm thinking I may use it in my GUI. I do plan on limiting my use of this method as I feel event driven design requires too much synchronization. All feedback is appreciated, thank you.

Share this post


Link to post
Share on other sites
Advertisement
If you're using this for GUI, then you won't benefit from multi-threading.

A lot of attempts have been made at thread-safe window managers, but ultimately, there's a "except..." case which breaks usefulness.

For this very reason, GUI frameworks use internal event pipeline that executes all events from a single thread. Even then, various problems arise. Or simply put: there are many hidden pitfalls when trying to do multi-threaded UIs.

When using implicit locking on every notification, the overhead is through the roof as soon as you have a moderate amount of resources.

But perhaps the greatest problem with this is that it's a dead-lock waiting to happen.

Button1--trigger-->B--trigger-->C // thread 1
Button2--trigger-->C--trigger-->B // thread 2



So you're left with two options: Using per object lock, resulting in above, or using global lock, which negates any benefit from multi-threading.

The only way to avoid this would be, well, what many window managers use - separate thread for event invocation.

And you're back to square one.

Long story short: Warning, there be dragons here. Boost folks aren't lazy. The reason it's not thread safe is due to design issues.

Share this post


Link to post
Share on other sites
Interesting, right now I am not sure if my GUI will need to be multi-threaded i just don't want to limit it unnecessarily. It seems that Qt has multi-threaded GUI handled well, although I don't want another library attached to my project, not for something with as limited use to my project as multi-threaded event handling.

My Window wrapper however, will need multi-threaded event handling.

If that is the only place that needs it then I should probably make a more closed system that only provides one "already defined for you" event listener type per event. That way I would limit the places where listener types could cause multi-threading issues.

Share this post


Link to post
Share on other sites
Your Delegate class depends on the listener class as a template parameter, which keeps it from being particularly useful. The ability to register a listener without inheriting from an explicit XyzListener class is the main advantage of delegates.

Share this post


Link to post
Share on other sites
^
Quote:
Original post by T1Oracle
I'm sure that this doesn't fully match the C# delegate spec...
However, this method does allow for me to make an event handler interface that lets me connect event listeners and automatically disconnect them when they go out of scope in a thread safe manner.

Regardless, I am about to restrict the "usefulness" even further in the name of thread safety by forcing users to use my XYZListener class instead being able to derive their own. For detecting things like changes in window dimensions, loss of window focus, and the closing of the application there isn't much need for offering that level of flexibility. Changes of window dimensions can be handled by doing an if (condition==true) and then explicitly copying the new dimensions by calling another class method if needed.

I see no such situation where more must be demanded of the event interface, to implement a useful functionality.

Share this post


Link to post
Share on other sites
Delegates area about organization, not functionality. If you don't want the convenience of a heterogeneous collection of listeners, why not just use a container of weak_ptrs to your listener interface?

Share this post


Link to post
Share on other sites
Quote:
Original post by T1OracleInteresting, right now I am not sure if my GUI will need to be multi-threaded i just don't want to limit it unnecessarily. It seems that Qt has multi-threaded GUI handled well, although I don't want another library attached to my project, not for something with as limited use to my project as multi-threaded event handling.


Qt performs all GUI work in a single thread. Threads may queue events through the main event loop but the events are dispatched on the main GUI thread. You can invoke Qt's signal/slot mechanism from multiple threads, but don't do any GUI work from those slots.

In short, Qt does not have a multi-threaded GUI.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
  • Advertisement
  • Popular Tags

  • Advertisement
  • Popular Now

  • Similar Content

    • By nihitori
      The Emotional Music Vol. I pack focuses on beautiful and esoteric orchestral music, capable of creating truly emotive and intimate moods. It features detailed chamber strings, cello and piano as the main instruments, resulting in a subtle and elegant sound never before heard in video game royalty-free music assets.

      The pack includes 5 original tracks, as well as a total of 47 loops based on these tracks (long loops for simple use and short loops for custom / complex music layering).

      Unity Asset Store link: https://www.assetstore.unity3d.com/en/#!/content/107032
      Unreal Engine Marketplace link: https://www.unrealengine.com/marketplace/emotional-music-vol-i

      A 15 seconds preview of each main track is available on Soundcloud:
       
    • By RoKabium Games
      Another one of our new UI for #screenshotsaturday. This is the inventory screen for showing what animal fossils you have collected so far. #gamedev #indiedev #sama
    • By eldwin11929
      We're looking for programmers for our project.
      Our project is being made in Unity
      Requirements:
      -Skills in Unity
      -C#
      -Javascript
      -Node.js
      We're looking for programmers who can perform a variety of functions on our project.
      Project is a top-down hack-and-slash pvp dungeon-crawler like game. Game is entirely multiplayer based, using randomized dungeons, and a unique combat system with emphasis on gameplay.
      We have a GDD to work off of, and a Lead Programmer you would work under.
      Assignments may include:
      -Creating new scripts of varying degrees specific to the project (mostly server-side, but sometimes client-side)
      -Assembling already created monsters/characters with existing or non-existing code.
      -Creating VFX
      -Assembling already created environment models
      If interested, please contact: eldwin11929@yahoo.com
      This project is unpaid, but with royalties.
       
      ---
      Additional Project Info:
      Summary:
      Bassetune Reapers is a Player-verus-Player, competitive dungeon crawler. This basically takes on aspects of dungeon crawling, but with a more aggressive setting. Players will have the option to play as the "dungeon-crawlers" (called the 'Knights', or "Knight Class", in-game) or as the "dungeon" itself (literally called the 'Bosses', or "Boss Class", in-game). What this means is that players can choose to play as the people invading the dungeon, or as the dungeon-holders themselves.
      Key Features:
      -Intense, fast-paced combat
      -Multiple skills, weapons, and ways to play the game
      -Tons of different Bosses, Minibosses, creatures and traps to utilize throughout the dungeon
      -Multiple unique environments
      -Interesting, detailed lore behind both the game and world
      -Intricate RPG system
      -Ladder and ranking system
      -Lots of customization for both classes s of customization for both classes
    • By RoKabium Games
      Custom coffee mugs have arrived... More caffeine!
      Have a great weekend everyone! 
      #gamedev #indiedev #sama #caffeine
    • By Atwo Studios
       
      Hey guys,

      Anthony here from Atwo Studios bringing you some new updates for the new year!
      In this video I go over our game ROY, the new games and some general updates to the company!

      If you have not checked out ROY feel free to give it a try! Many people have said they enjoyed the game thus far!
      ROY: https://goo.gl/o6JJ5P
       
  • Advertisement