如何强制归档程序 (ar) 生成与目标文件行为相同的归档文件?
How to force archiver (ar) to produce archive that behaves identically to object file?
我使用一种相当复杂的方法来使用 "catalog" 方法进行对象分配。该代码生成一个目录对象(全局静态映射),该对象由指向派生对象的基指针填充。这都是亵渎神明的,但重点不是这里。程序执行时,目录由所有派生对象填充,我们可以分配在目录中注册的任何内容,而无需维护候选派生列表 类。伟大的。但是,当派生的 类 之一是 运行 通过归档程序 (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;
}
和生成文件:
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
所以链接了两个可执行文件。第一个(测试)与目标文件链接并产生 "correct" 结果:
$ ./test
Registered Solver: Derived
Creating solver of type: Derived name Foo
第二个 (testlib) 与替换为 Derived.a 的 Derived.o 文件链接,该文件是使用 "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" 足以知道它从目标文件中剥离了什么。所以有两个问题:
- 为什么归档版本失败?
- 如何生成从链接器角度看与目标文件相同的存档文件?
问题不在于存档器,而在于链接器。链接器获取每个目标文件,加上存档中需要的内容。您存档的成员不解析任何未解析的引用,因此不需要。
gnu 链接器理解 --whole-archive
,这正是您在这里的意图。
我使用一种相当复杂的方法来使用 "catalog" 方法进行对象分配。该代码生成一个目录对象(全局静态映射),该对象由指向派生对象的基指针填充。这都是亵渎神明的,但重点不是这里。程序执行时,目录由所有派生对象填充,我们可以分配在目录中注册的任何内容,而无需维护候选派生列表 类。伟大的。但是,当派生的 类 之一是 运行 通过归档程序 (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;
}
和生成文件:
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
所以链接了两个可执行文件。第一个(测试)与目标文件链接并产生 "correct" 结果:
$ ./test
Registered Solver: Derived
Creating solver of type: Derived name Foo
第二个 (testlib) 与替换为 Derived.a 的 Derived.o 文件链接,该文件是使用 "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" 足以知道它从目标文件中剥离了什么。所以有两个问题:
- 为什么归档版本失败?
- 如何生成从链接器角度看与目标文件相同的存档文件?
问题不在于存档器,而在于链接器。链接器获取每个目标文件,加上存档中需要的内容。您存档的成员不解析任何未解析的引用,因此不需要。
gnu 链接器理解 --whole-archive
,这正是您在这里的意图。