LoginSignup
2
2

More than 5 years have passed since last update.

メンバ関数ポインタへのコールバックをtemplateで

Posted at

 動機

templateでメンバ関数ポインタへのコールバックってどうやるんだろうと思ったので。

やりたいこと

  1. メンバ関数ポインタをtraitsで保持したい。(でもオブジェクト保持はめんどくさいのでSingleton前提にしている)
  2. コンストラクタでnewはしたくない
  3. コールバックを要求する側でtraitsを定義したらそれに応じてコールバックする側は変更なしでattachしたい
  4. コールバックの要素数はコンパイル時に決定したい

コード

Caster.hpp
#pragma once
#include "Singleton.hpp"
using namespace std;


struct Listner
{
public:
    Listner() {}
    virtual ~Listner(){}
    virtual void execute() = 0;

};

enum ListnerName
{
    Listner_start = -1,
    Listner1_event1 = 0,
    Listner1_event2,
    Listner_end,
};

class Caster
{
public:
    Caster()
    {

    }
    bool initialize()
    {
        return initialize<Listner_start>();
    }

    void run()
    {
        for (int i = 0; i < Listner_end; ++i)
        {
            listners_[i]->execute();
        }
    }
    ~Caster()
    {
        for (int i = 0; i < Listner_end; ++i)
        {
            delete listners_[i];
        }
    }

private:
    template<ListnerName N>
    bool initialize();
    template<>
    bool initialize<Listner_start>()
    {
        const ListnerName next = static_cast<ListnerName>(Listner_start + 1);
        return initialize<next>();
    }
    template<>
    bool initialize<Listner_end>()
    {
        return true;
    }
    template <typename T>
    class ConcreteListner : public Listner
    {
    public:
        ConcreteListner(void(T::*func)())
            : func_(func) {}
        ~ConcreteListner(){}
        void execute() { (T::getInstance()->*func_)(); }
    private:
        void (T::*func_)();
    };

    Listner* listners_[Listner_end];
};

template<ListnerName N> struct listner_traits;

template<ListnerName N>
bool Caster::initialize()
{
    if ((listners_[N] = new(std::nothrow) ConcreteListner<listner_traits<N>::object_t>(listner_traits<N>::cb)) == 0)
    {
        return false;
    }
    return initialize<static_cast<ListnerName>(N+1)>();
}

Listner1.hpp
#pragma once
#include <iostream>
#include "Singleton.hpp"
using namespace std;


class Listner1 : public Singleton<Listner1>
{
public:
    void event1()
    {
        cout << "event1" << endl;
    }
    void event2()
    {
        cout << "event2" << endl;
    }
};

template<>
struct listner_traits<Listner1_event1>
{
    typedef Listner1 object_t;
    typedef void(Listner1::*callBack)();
    static const callBack cb;
};
const listner_traits<Listner1_event1>::callBack listner_traits<Listner1_event1>::cb = &Listner1::event1;

template<>
struct listner_traits<Listner1_event2>
{
    typedef Listner1 object_t;
    typedef void(Listner1::*callBack)();
    static const callBack cb;
};
const listner_traits<Listner1_event2>::callBack listner_traits<Listner1_event2>::cb = &Listner1::event2;


Singleton.hpp
#pragma once

template <typename T>
class Singleton
{
protected:
    Singleton(){}
    Singleton(Singleton const&);
    ~Singleton(){}

    Singleton& operator=(Singleton const&);

public:
    static T* getInstance()
    {
        static T i;
        return &i;
    }
};

main.cpp
#include "Singleton.hpp"
#include "Caster.hpp"
#include "Listner1.hpp"
using namespace std;


int main(int argv, char* argc[])
{
    Caster caster;
    caster.initialize();
    caster.run();
    return 0;
}
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2