Как заставить архиватор (ar) создавать архив, который ведет себя идентично объектному файлу?

Я использую довольно сложный подход к размещению объектов, используя подход «каталога». Код создает объект каталога (глобальную статическую карту), который заполняется базовыми указателями, указывающими на производные объекты. Это все кощунственно, но не в этом дело. После выполнения программы каталог заполняется всеми производными объектами, и мы можем выделить все, что было зарегистрировано в каталоге, без необходимости поддерживать список производных классов-кандидатов. Здорово. Однако, когда один из производных классов запускается через архиватор (ar) и архив используется вместо объектного файла для компоновщика, код дает сбой. Может кто-нибудь сказать мне, почему это происходит? Пример кода:

//Base.h
#include<unordered_map>
#include<string>
#include<map>
#include<iostream>
#include<memory>

class Base
{
public:
  Base( std::string name ):m_name(name){}
  virtual ~Base() {}
  std::string m_name;
};

class ObjectCatalogueEntryBase
{
public:
  typedef std::map< std::string, ObjectCatalogueEntryBase* > CatalogueType;

  ObjectCatalogueEntryBase(){}
  virtual std::unique_ptr<Base> Allocate( std::string const & ) = 0;
  virtual ~ObjectCatalogueEntryBase(){}

  static CatalogueType& GetCatalogue()
  {
    static CatalogueType catalogue;
    return catalogue;
  }

  static std::unique_ptr<Base> Factory( const std::string& objectTypeName, std::string const & name )
  {
    std::cout<<"Creating solver of type: "<<objectTypeName<<" name "<<name<<std::endl;
    ObjectCatalogueEntryBase* const  entry = GetCatalogue().at(objectTypeName);
    return entry->Allocate(name);
  }

};


template< typename TYPE >
class ObjectCatalogueEntry : public ObjectCatalogueEntryBase
{
public:
  ObjectCatalogueEntry():
    ObjectCatalogueEntryBase()
  {
    std::string name = TYPE::CatalogueName();
    (ObjectCatalogueEntryBase::GetCatalogue())[name] = this;
    std::cout<<"Registered Solver: "<<name<<std::endl;
  }

  ~ObjectCatalogueEntry() final{}

  virtual std::unique_ptr<Base> Allocate( std::string const & name) final
  {
    return std::unique_ptr<Base>(new TYPE(name));
  }
};

/// Compiler directive to simplify autoregistration
#define REGISTER_FACTORY( ClassName) namespace{ ObjectCatalogueEntry<ClassName> reg_; }

следующий файл:

// Derived.h
#include "Base.h"
class Derived : public Base
{
public:
  Derived( std::string name );
  ~Derived();
  static std::string CatalogueName() {return "Derived";}
};

следующий файл:

// Derived.cpp
#include "Derived.h"

Derived::Derived( std::string name):Base(name)
{}

Derived::~Derived()
{}

REGISTER_FACTORY(Derived)

следующий файл:

// main.cpp
#include "Derived.h"

int main()
{
  std::string newName("Foo");
  auto solver = ObjectCatalogueEntryBase::Factory(Derived::CatalogueName(),newName);
  return 0;
}

И Makefile:

CPP=g++-mp-6

test: main.o Derived.o
    ${CPP} -std=c++14 -o test    main.o Derived.o

testlib: main.o Derived.a
    ${CPP} -std=c++14 -o testlib main.o Derived.a

main.o: main.cpp Base.h Derived.h
    ${CPP} -std=c++14 -c main.cpp 

Derived.o: Derived.cpp Derived.h
    ${CPP} -std=c++14 -c Derived.cpp 

Derived.a:
    ar qcsv Derived.a  Derived.o

clean:
    rm *.o *.a test testlib

all: test testily

Итак, два исполняемых файла связаны. Первый (тестовый) связан с объектными файлами и выдает «правильный» результат:

$ ./test
Registered Solver: Derived
Creating solver of type: Derived name Foo

Второй (testlib) связан с файлом Derived.o, замененным файлом Derived.a, который был создан с помощью «ar», используя только Derived.o. Результат:

./testlib 
Creating solver of type: Derived name Foo
terminate called after throwing an instance of 'std::out_of_range'
  what():  map::at
Abort trap: 6

Очевидно, регистрация здесь не происходила, и карта пуста. Тот же результат с gcc6 и apple clang7. Я подозреваю, что это как-то связано с глобальной статической картой, но я недостаточно понимаю "ar", чтобы знать, что он удаляет из объектного файла. Итак, есть два вопроса:

  1. почему архивная версия не работает?
  2. как я могу создать файл архива, который будет выглядеть так же, как объектный файл с точки зрения компоновщика?

person doc07b5    schedule 22.06.2016    source источник


Ответы (1)


Проблема не в архиваторе, а в компоновщике. Компоновщик берет каждый объектный файл, а также то, что необходимо, из архивов. Члены вашего архива не разрешают неразрешенные ссылки и поэтому не нужны.

Компоновщик gnu понимает --whole-archive, что вы и имели в виду.

person MSalters    schedule 22.06.2016
comment
В darwin (mac osx) системный компоновщик не имеет --whole-archive. Вместо этого есть команда -all_load, которая работает. Спасибо! - person doc07b5; 22.06.2016