C ++宏定义彼此依赖的类

时间:2015-07-14 18:24:05

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

快速使用示例:

#ifndef SOME_HPP
#define SOME_HPP

signal(AA, aa)

class X : public AASender {
};

#endif

如果在一些.cpp文件中包含(可能是间接的)类似的东西,它会收到类似的错误: “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)

1 个答案:

答案 0 :(得分:3)

标头中的外部函数定义将与包含该标头的每个编译单元一起发出,因此具有多个定义。只需将其设为inline即可避免这种情况。

顺便说一下,这与您的宏无关。在C ++中应该避免使用宏。除了包含警卫和条件编译之外,C ++中几乎没有明智的宏用法。使用模板可以做得更好。