C++宏定义相互依赖的类

标签 c++ macros

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

#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中。当最后一节取消注释时,我会收到很多“多重定义”错误。我相信我知道为什么它不起作用:最后一部分算作定义,不应该在标题中。但是我怎样才能实现这样的宏呢?或者,如果我弄错了,真正的问题是什么?

快速使用示例:

#ifndef SOME_HPP
#define SOME_HPP

signal(AA, aa)

class X : public AASender {
};

#endif

如果在一些 .cpp 文件中包含(可能是间接的)类似的内容,它会出现类似的错误: “AAReceiver 的 typeinfo 的多个定义,首先在此处定义,在函数中~new_allocator':”。 “typeinfo”因析构函数和类型名称和 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 中的外联函数定义将随包含该 header 的每个编译单元一起发出,因此会出现多个定义。只需将其设为内联即可避免这种情况。

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

关于C++宏定义相互依赖的类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31414423/

相关文章:

C 宏仅更改函数调用而不更改函数定义

c++ - 私有(private)数据成员访问

c++ - 如何动态更改 CMFCRibbonLabel 的文本

c++ - 编译器忽略我的包含 -I 新库版本

c++ - 我如何才能知道是否在最大水平上使用我的所有核心

c++ - 使用#pragma 和#ifdef 的多行宏

c - 如何在运行时更改宏定义

c++ - 如果我为一个类写new和delete运算符,我是否必须写所有它们的重载?

regex - 我想从工作区中的一个文件中搜索第 n 行,并将其替换为另一个文件中保存的第 n 行

c++ - 结合 CMake option() 与 add_definitions()