【问题标题】:Implementing Signal template in C++在 C++ 中实现信号模板
【发布时间】:2018-04-03 00:01:20
【问题描述】:

我正在尝试在 C++ 中实现 Signal 模板。

这是我目前所拥有的:

Main.cpp:

//Developed by Trofimov Yaroslav on 02.04.18

#include <iostream>
#include "Signal.h"

void f1() {
    std::cout << "here in f1" << std::endl;
}
void f2() {
    std::cout << "F2 F2 F2" << std::endl;
}

typedef void (* VoidResultDelegate)();

int main(void) {
    Signal<VoidResultDelegate> signalVoid;
    signalVoid.addListener(f1);
    signalVoid.addListener(f1);
    signalVoid.invoke();
    signalVoid.removeListener(f2);
    signalVoid.invoke();
    return 0;
}

Signal.h:

//Developed by Trofimov Yaroslav on 02.04.18

#ifndef _SIGNAL_H_TROFIMOV_
#define _SIGNAL_H_TROFIMOV_
#include "LinkedList.h"

template<typename FunctionType>
class Signal {
    LinkedList<FunctionType> _delegates;

public:
    Signal<FunctionType>(void) 
        : _delegates(LinkedList<FunctionType>()) {

    }
    ~Signal<FunctionType>(void) {

    }
    void addListener(const FunctionType& delegated) {
        _delegates.add(delegated);
    }
    void removeListener(const FunctionType& delegated) {
        _delegates.remove(delegated);
    }
    void invoke() {
        _delegates.startIteration();
        while(_delegates.hasNext()) {
            _delegates.next()();
        }
    }
};

#endif

LinkedList.h:

//Developed by Trofimov Yaroslav on 30.03.2018

#ifndef _LINKED_LIST_H_TROFIMOV_
#define _LINKED_LIST_H_TROFIMOV_

#include <string>
#include <iostream>
#include <typeinfo>

template<typename T>
class LinkedList {
    template<typename T>
    struct Node {
        T _data;
        Node* _next;
        Node()
            : _next(0){}
        ~Node<T>() {
            if(_next) {
                delete _next; _next = 0;
            }
        }
    };
    Node<T>* _head;
    Node<T>* _tail;
    Node<T>* _iterator;

public:
    LinkedList<T>() 
        : _head(0), _tail(0), _iterator(0) {

    };
    ~LinkedList<T>() {
        delete _head; _head = 0;
    }
    void add(const T& element) {
        if(!_head) {
            _head = new Node<T>;
            _head->_data = element;
            _tail = _head;
            return;
        }

        Node<T>* newNode = new Node<T>;
        newNode->_data = element;
        _tail->_next = newNode;
        _tail = newNode;
        return;
    }
    void remove(const T& element) {
        if(!_head) {
            return;
        }
        if(_head->_data == element) {
            _head = _head->_next;
            return;
        }

        Node<T>* previous = _head;
        Node<T>* current = _head->_next;
        while(current) {
            if(current->_data == element) {
                previous->_next = current->_next;
                return;
            }

            previous = current;
            current = current->_next;
        }
    }
    void startIteration() {
        _iterator = _head;
    }
    bool hasNext() {
        return (_iterator)?true:false;
    }
    T& next() {
        T& res = _iterator->_data;
        _iterator = _iterator->_next;
        return res;
    }
};

#endif

所以,我想补充的是一种通用的参数传递方式。假设现在我有typedef void (* VoidResultDelegate)(int i); 而不是typedef void (* VoidResultDelegate)();,这意味着我希望int 参数以某种方式出现在Signal::invoke 方法参数列表中,并以_delegates.next()(); 的方式传递到这里_delegates.next()(i); 或类似的东西。

在 C++ 中是否有可能?

我正在考虑将另一个typename 参数传递给Signal,它表示Signal::invoke 接受的参数类型,并在调用_delegates.next()(); 中传递给链表中的元素。但是这种方法的问题是我无法控制参数的数量(它将只有一个参数)。并且没有人(当然我指的是编译器)强制我将正确的参数作为typename 传递给Signal 模板。在上面的示例中,我可以通过 bool typename 而不是 int 并且在出现错误之前没有人会注意到它。

这里是回答后的更新版本:

Main.cpp

//Developed by Trofimov Yaroslav on 02.04.18

#include <iostream>
#include "Signal.h"

void f1(int i) {
    std::cout << "here in f1" << std::endl;
}
void f2(int i) {
    std::cout << "F2 F2 F2" << std::endl;
}

typedef void (* VoidResultDelegate)(int i);

int main(void) {
    Signal<VoidResultDelegate, int> signalVoid;
    signalVoid.addListener(f1);
    signalVoid.addListener(f2);
    signalVoid.invoke(-1);
    signalVoid.removeListener(f1);
    signalVoid.invoke(-1);
    return 0;
}

Signal.h

//Developed by Trofimov Yaroslav on 02.04.18

#ifndef _SIGNAL_H_TROFIMOV_
#define _SIGNAL_H_TROFIMOV_
#include "LinkedList.h"

template<typename FunctionType, typename... Args>
class Signal {
    LinkedList<FunctionType> _delegates;

public:
    Signal<FunctionType, parameter>(void) 
        : _delegates(LinkedList<FunctionType>()) {

    }
    ~Signal<FunctionType, parameter>(void) {

    }
    void addListener(const FunctionType& delegated) {
        _delegates.add(delegated);
    }
    void removeListener(const FunctionType& delegated) {
        _delegates.remove(delegated);
    }
    void invoke(Args&& ... args) {
        _delegates.startIteration();
        while(_delegates.hasNext()) {
            (_delegates.next())(std::forward<Args>(args)...);
        }
    }
};

#endif

LinkedList.h:

//Developed by Trofimov Yaroslav on 30.03.2018

#ifndef _LINKED_LIST_H_TROFIMOV_
#define _LINKED_LIST_H_TROFIMOV_

#include <string>
#include <iostream>
#include <typeinfo>

template<typename T>
class LinkedList {
    template<typename T>
    struct Node {
        T _data;
        Node* _next;
        Node()
            : _next(0){}
        ~Node<T>() {
            if(_next) {
                delete _next; _next = 0;
            }
        }
    };
    Node<T>* _head, _tail, _iterator;

public:
    LinkedList<T>() 
        : _head(0), _tail(0), _iterator(0) {

    };
    ~LinkedList<T>() {
        delete _head; _head = 0;
    }
    void add(const T& element) {
        if(!_head) {
            _head = new Node<T>;
            _head->_data = element;
            _tail = _head;
            return;
        }

        Node<T>* newNode = new Node<T>;
        newNode->_data = element;
        _tail->_next = newNode;
        _tail = newNode;
        return;
    }
    void remove(const T& element) {
        if(!_head) {
            return;
        }
        if(_head->_data == element) {
            _head = _head->_next;
            return;
        }

        Node<T>* previous = _head;
        Node<T>* current = _head->_next;
        while(current) {
            if(current->_data == element) {
                previous->_next = current->_next;
                return;
            }

            previous = current;
            current = current->_next;
        }
    }
    void startIteration() {
        _iterator = _head;
    }
    bool hasNext() {
        return (_iterator)?true:false;
    }
    T& next() {
        T& res = _iterator->_data;
        _iterator = _iterator->_next;
        return res;
    }
};

#endif

【问题讨论】:

    标签: c++ function types parameters signals


    【解决方案1】:

    您可以对 Signal 类进行部分特化,以在单独的模板参数中获取返回类型和参数。

    // Declare the template without any definition
    template<typename FunctionType>
    class Signal;
    
    // Add partial specialization
    template<typename ReturnType, typename... Args>
    class Signal<ReturnType(*)(Args...)> {
        // Now you have access to return type and arguments
    
        // Other things...
    
        void invoke(Args... args) {
            _delegates.startIteration();
            while(_delegates.hasNext()) {
                _delegates.next()(args...);
            }
        }
    }
    

    为了简单起见,我从 invoke 函数中省略了 perfect forwarding。有了它,它看起来像这样。

    void invoke(Args&&... args) {
        _delegates.startIteration();
        while(_delegates.hasNext()) {
            _delegates.next()(std::forward<Args>(args)...);
        }
    }
    

    【讨论】:

    • 我在上面发布了一个更新版本。我做了你提议的事吗?如果是这样,我现在遇到了那个奇怪的错误:signal.h(7): error C2143: syntax error : missing ',' before '...' 你能帮帮我吗?
    • @ggghahaha 如果您看到我的示例,您需要先声明没有任何定义且只有 1 个模板参数的模板,然后是特化。请注意专业化如何具有class Signal&lt;ReturnType(*)(Args...)&gt; ...。但是您的错误消息不是由于这个原因。你用的是什么编译器,什么版本?
    • @ggghahaha 模板参数包是c++11 功能,因此您需要在至少启用 c++11 的情况下进行编译。所有当前的 VS 版本都应该能够做到这一点,但我不确定它是否默认启用。可能需要在某处激活它。