我正在尝试编写一次定义两个紧密连接的类的宏,但我不能。这是我的代码:
#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)
答案 0 :(得分:3)
标头中的外部函数定义将与包含该标头的每个编译单元一起发出,因此具有多个定义。只需将其设为inline
即可避免这种情况。
顺便说一下,这与您的宏无关。在C ++中应该避免使用宏。除了包含警卫和条件编译之外,C ++中几乎没有明智的宏用法。使用模板可以做得更好。