【发布时间】:2020-11-10 11:29:03
【问题描述】:
我们有一些代码可以根据模板类定义分派一个“网关”对象。简化版如下
首先是工厂定义:Factory.h
#pragma once
#include <map>
#include <string>
class BaseGateway
{
public:
virtual void hello() = 0;
protected:
virtual ~BaseGateway() = default;
};
template <typename T>
class Singleton
{
public:
template <typename DERIVED_T>
inline static DERIVED_T& CreateInstance() {
if (ms_pInstance == nullptr) {ms_pInstance = new DERIVED_T();}
return static_cast<DERIVED_T&>(*ms_pInstance);
}
protected:
static inline T* ms_pInstance;
Singleton() {}
virtual ~Singleton() = default;
};
class Gateway : public Singleton<Gateway>, public BaseGateway
{
protected:
friend class Singleton<Gateway>; // for access to private ctor
Gateway() = default;
virtual ~Gateway() = default;
public:
template<typename GatewayTraits>
inline static const char *GetName() {
return GatewayTraits::GetName();
}
};
class Factory
{
public:
using Dispatcher = std::map<std::string, BaseGateway*(*)()>;
static Dispatcher & GetDispatcher() {
static Dispatcher dispatcher;
return dispatcher;
}
};
template <typename GatewayTraits>
struct Dispatcher
{
static inline struct EntryInserter
{
EntryInserter() {
Factory::GetDispatcher().insert(
{
Gateway::GetName<GatewayTraits>(),
[]() -> BaseGateway*
{ return &Gateway::CreateInstance<typename GatewayTraits::GatewayType>(); }
});
}
} m_EntryInserter;
virtual ~Dispatcher() {
(void)&m_EntryInserter;
}
};
然后是网关模板:Gateway.h
#include <iostream>
#include <string>
#include "Factory.h"
template<typename SpecificTraits>
class GenericGateway : public Gateway, public Dispatcher<SpecificTraits>
{
protected:
GenericGateway() = default;
~GenericGateway() override = default;
public:
void hello() override { SpecificTraits::ProcessAndDisplay(m_value); }
private:
typename SpecificTraits::ValueType m_value;
};
template<typename GatewayT>
struct IntTraits
{
using GatewayType = GatewayT;
using ValueType = int;
static const char *GetName() { return "int_gateway"; }
static void ProcessAndDisplay(ValueType &v) {
v = 0;
std::cout << v << std::endl;
}
};
class IntGateway : public GenericGateway<IntTraits<IntGateway>>
{
protected:
friend class Singleton<Gateway>;
IntGateway();
~IntGateway() override = default;
};
最后是实现和主要功能:
Impl.cpp
#include "Gateway.h"
IntGateway::IntGateway() = default;
Main.cpp
#include <cassert>
#include "Factory.h"
int main() {
auto iter = Factory::GetDispatcher().find("int_gateway");
assert(iter != Factory::GetDispatcher().end());
auto * gateway = iter->second();
gateway->hello();
}
正常编译链接没问题(程序输出0):
g++ -std=c++17 -static-libstdc++ -c Main.cpp && g++ -std=c++17 -static-libstdc++ -c Impl.cpp
g++ -std=c++17 -static-libstdc++ Impl.o Main.o && ./a
但是如果我把 Impl.o 放到一个静态库中,那就不行了:
ar qc libtest.a Impl.o && g++ -std=c++17 -static-libstdc++ libtest.a Main.o && ./a
显然,EntryInserter 被编译为弱符号,并且在链接期间被省略。我不明白为什么链接对象与从 .a 链接对象的行为不同
我在 Linux(clang 10 和 gcc 10)和 Cygwin(gcc 9)上对此进行了测试,它们似乎产生了相同的结果
【问题讨论】: