C++ 宏定义 类 相互依赖

C++ macro defining classes dependent on each other

我正在尝试编写同时定义两个紧密连接的 类 的宏,但我做不到。这是我的代码:

#ifndef SIGNALS_HPP
#define SIGNALS_HPP

#include <unordered_set>
#include <cstdio>

/**
  * Macro defines pair of classes, sender and receiver. When one of them is destroyed, 
  * all it's connections are dully unconnected.
  * 
  * EXAMPLE:
  * 
  *     signal(SomeEvent, some_event_occured)
  * 
  * EXPANDS TO:
  *     
  *     class SomeEventReceiver {
  *     protected:
  *         virtual void on_some_event_occured() {}
  *     }
  * 
  *     class SomeEventSender {
  *     public:
  *         void connect_some_event_occured(SomeEventReceiver & listener);  // registers listener
  *         void unconnect_some_event_occured(SomeEventReceiver & listener);  // forgets listener
  *     
  *     protected:
  *         void send_some_event_occured();  // notifies all listeners
  *     }
  */

#define signal(signal_name, slot_name)\
    class signal_name##Sender;\
    \
    class signal_name##Receiver\
    {\
        friend class signal_name##Sender;\
        std::unordered_set<signal_name##Sender*> senders;\
        \
    protected:\
        virtual void on_##slot_name() {}\
        \
    public:\
        virtual ~signal_name##Receiver();\
    };\
    \
    \
    class signal_name##Sender\
    {\
        friend class signal_name##Receiver;\
        std::unordered_set<signal_name##Receiver*> listeners;\
        \
    public:\
        virtual ~signal_name##Sender()\
        {\
            for (auto i : listeners)\
                i->senders.erase(this);\
            listeners.clear();\
        }\
        \
        void connect_##slot_name(signal_name##Receiver & listener)\
        {\
            listeners.insert(&listener);\
            listener.senders.insert(this);\
        }\
    \
        void unconnect_##slot_name(signal_name##Receiver & listener)\
        {\
            listeners.erase(&listener);\
            listener.senders.erase(this);\
        }\
        \
    protected: \
        void send_##slot_name()\
        {\
            for (auto i : listeners)\
                i->on_##slot_name();\
        }\
        \
    };\
    \
    /*\ //  <--------------  THE LAST SECTION
    signal_name##Receiver::~signal_name##Receiver()\
    {\
        for (auto i : senders)\
            i->listeners.erase(this);\
        senders.clear();\
    }//*/\




#endif // SIGNALS_HPP

1) 为什么它与评论的最后一节一起工作?我认为所有的虚拟方法都必须是定义的或纯的?

2) 这个宏在另一个header中使用。当最后一部分取消注释时,我收到很多 "multiple definition" 错误。我相信我知道为什么它不起作用:最后一部分算作定义,不应该在 header 中。但是我怎样才能实现这样的宏呢?或者,如果我弄错了,真正的问题是什么?

快速使用示例:

#ifndef SOME_HPP
#define SOME_HPP

signal(AA, aa)

class X : public AASender {
};

#endif

如果在一些 .cpp 文件中包含(可能间接地)类似的内容,则会出现此类错误: "multiple definition of typeinfo for AAReceiver', first defined here, In function~new_allocator':"。 "typeinfo" varies to destructor and键入名称和 vtable。AAReciver 有时会变成 AASender。

如果有人真的喜欢阅读,那就是真正的错误:

army.o: In function `~BattleEndaaaaaaaReceiver':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `BattleEndaaaaaaaReceiver::~BattleEndaaaaaaaReceiver()'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
army.o: In function `~new_allocator':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `BattleEndaaaaaaaReceiver::~BattleEndaaaaaaaReceiver()'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
army.o: In function `~new_allocator':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `BattleEndaaaaaaaReceiver::~BattleEndaaaaaaaReceiver()'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
army.o: In function `std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<BattleEndaaaaaaaSender*, false> > >::_M_deallocate_nodes(std::__detail::_Hash_node<BattleEndaaaaaaaSender*, false>*)':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `typeinfo for BattleEndaaaaaaaReceiver'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
army.o: In function `~new_allocator':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `typeinfo name for BattleEndaaaaaaaReceiver'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
army.o: In function `~new_allocator':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `vtable for BattleEndaaaaaaaReceiver'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
game.o: In function `~BattleEndaaaaaaaReceiver':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `BattleEndaaaaaaaReceiver::~BattleEndaaaaaaaReceiver()'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
game.o: In function `new_allocator':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `BattleEndaaaaaaaReceiver::~BattleEndaaaaaaaReceiver()'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
game.o: In function `new_allocator':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `BattleEndaaaaaaaReceiver::~BattleEndaaaaaaaReceiver()'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
game.o: In function `vector':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `typeinfo for BattleEndaaaaaaaReceiver'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
game.o: In function `new_allocator':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `typeinfo name for BattleEndaaaaaaaReceiver'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
game.o: In function `new_allocator':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `vtable for BattleEndaaaaaaaReceiver'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
gamemapwidget.o: In function `~BattleEndaaaaaaaReceiver':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `BattleEndaaaaaaaReceiver::~BattleEndaaaaaaaReceiver()'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
gamemapwidget.o: In function `new_allocator':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `BattleEndaaaaaaaReceiver::~BattleEndaaaaaaaReceiver()'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
gamemapwidget.o: In function `new_allocator':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `BattleEndaaaaaaaReceiver::~BattleEndaaaaaaaReceiver()'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
gamemapwidget.o: In function `std::_Hashtable<BattleEndaaaaaaaReceiver*, BattleEndaaaaaaaReceiver*, std::allocator<BattleEndaaaaaaaReceiver*>, std::__detail::_Identity, std::equal_to<BattleEndaaaaaaaReceiver*>, std::hash<BattleEndaaaaaaaReceiver*>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, true, true> >::_M_erase(std::integral_constant<bool, true>, BattleEndaaaaaaaReceiver* const&)':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `typeinfo for BattleEndaaaaaaaReceiver'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
gamemapwidget.o: In function `new_allocator':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `typeinfo name for BattleEndaaaaaaaReceiver'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
gamemapwidget.o: In function `new_allocator':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `vtable for BattleEndaaaaaaaReceiver'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
battle.o: In function `~BattleEndaaaaaaaReceiver':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `BattleEndaaaaaaaReceiver::~BattleEndaaaaaaaReceiver()'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
battle.o: In function `~new_allocator':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `BattleEndaaaaaaaReceiver::~BattleEndaaaaaaaReceiver()'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
battle.o: In function `~new_allocator':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `BattleEndaaaaaaaReceiver::~BattleEndaaaaaaaReceiver()'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
battle.o: In function `std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<BattleEndaaaaaaaSender*, false> > >::_M_deallocate_nodes(std::__detail::_Hash_node<BattleEndaaaaaaaSender*, false>*)':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `typeinfo for BattleEndaaaaaaaReceiver'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
battle.o: In function `~new_allocator':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `typeinfo name for BattleEndaaaaaaaReceiver'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
battle.o: In function `~new_allocator':
/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: multiple definition of `vtable for BattleEndaaaaaaaReceiver'
province.o:/home/michal/Dokumenty/Projekty/build-Universania-Desktop-Debug/../Universania/battle.hpp:10: first defined here
clang: error: linker command failed with exit code 1 (use -v to see invocation)

header 中的 out-of-line 函数定义将与包含该 header 的每个编译单元一起发出,因此有多个定义。只需将其设置为 inline 即可避免这种情况。

顺便说一句,这与您的宏无关。在 C++ 中应避免使用宏。除了 include guards 和条件编译,C++ 中几乎没有合理的宏用法。使用模板可以更好地完成许多事情。