2016-06-22 13 views
0

나는 "카탈로그"접근법을 사용하여 객체 할당에 다소 관여하는 접근법을 사용합니다. 코드는 파생 된 객체를 가리키는 기본 포인터로 채워지는 카탈로그 객체 (전역 정적지도)를 생성합니다. 이것은 모두 불경 스럽지만 여기에 요점은 아닙니다. 프로그램 실행시, 카탈로그는 모든 파생 된 오브젝트로 채워지며 후보 파생 된 클래스 목록을 유지할 필요없이 카탈로그에 등록 된 모든 것을 할당 할 수 있습니다. 큰. 그러나 파생 클래스 중 하나가 아카이버 (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; 
} 

그리고 메이크 :

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 

그래서 두 개의 실행 파일이 링크됩니다. 첫번째 (시험) 오브젝트 파일과 연계하여 "올바른"결과 생성된다으로 만들어진 Derived.a로 대체 Derived.o 파일과 연결된

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

두번째 (testlib)를 Derived.o만을 사용하는 "ar". 결과는 다음과 같습니다.

./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. 링커 관점에서 볼 때 객체 파일과 동일한 모양의 아카이브 파일을 생성하려면 어떻게해야합니까?

답변

1

문제는 아카이버가 아니라 링커입니다. 링커는 모든 오브젝트 파일과 아카이브에서 필요한 것을 취합니다. 보관소 회원은 해결되지 않은 참조를 해결하지 않으므로 필요하지 않습니다.

gnu 링커는 여기 의도 한대로 --whole-archive을 인식합니다.

+0

darwin (mac osx)에서 시스템 링커에는 "--whole-archive"가 없습니다. 대신 작동하는 것처럼 보이는 "-all_load"명령이 있습니다. 감사! – doc07b5