【问题标题】:gcc: weak symbols compiled from template static member in static libraries seems lost during linkgcc:从静态库中的模板静态成员编译的弱符号在链接期间似乎丢失了
【发布时间】: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)上对此进行了测试,它们似乎产生了相同的结果

【问题讨论】:

    标签: c++ c++17


    【解决方案1】:

    这与弱符号无关。

    我不明白为什么链接对象与从 .a 链接对象的行为不同

    因为这就是链接的工作原理。

    当您直接链接到 .o 文件时,其所有符号都包含在链接中,无论它们是否解析未定义的引用。

    但是对于一个库来说,如果没有对符号的未定义引用,那么这个库就不会被使用。

    Main.o 中的任何内容都没有引用Impl.o 中定义的任何内容,因此没有未定义的引用。因此,当Impl.o 中的符号在静态库中定义时,它们不会添加到可执行文件中。

    此外,库需要使用它们的对象之后列出,否则链接器将忽略该库。见http://c-faq.com/lib/libsearch.html

    为确保静态库包含在您的链接中,您可以执行以下操作:

    g++ -std=c++17 -static-libstdc++ Main.o -Wl,--whole-archive libtest.a -Wl,--no-whole-archive 
    

    【讨论】:

      猜你喜欢
      • 2015-04-05
      • 2012-10-16
      • 2012-10-06
      • 1970-01-01
      • 1970-01-01
      • 2021-11-06
      • 2018-12-06
      • 1970-01-01
      相关资源
      最近更新 更多