Слабое определение функции во время компоновки и тестирование на переопределение

(Среда: gcc 4.6, c ++ 11, glibc 2.13, binutils 2.21)

Пожалуйста, обратите внимание на следующую демонстрацию "связывания" в качестве фона:

foo.h:

#pragma once
#include <iostream>

void foo();

foo1.cpp:

#include "foo.h"

void foo()
{
    std::cout << "foo1";
}

foo2.cpp:

#include "foo.h"

void foo()
{
    std::cout << "foo2";
}

main.cpp:

#include "foo.h"

int main()
{
    foo();
}

Makefile:

compile: main.o foo1.o foo2.o

case1: compile
    g++ -o case1.x main.o

case2: compile
    g++ -o case2.x main.o foo1.cpp

case3: compile
    g++ -o case3.x main.o foo1.o foo2.o

clean:
    rm *.o *.x

У нас есть следующее объявление функции:

void foo();

и у нас есть несколько его определений, одно в foo1.cpp, а другое в foo2.cpp.

Компилируем файлы .cpp в файлы .o:

$ make compile

Нет проблем, создаются main.o, foo1.o и foo2.o.

Случай 1

Затем мы пытаемся связать только main.o:

$ make case1
error: undefined reference to 'foo()'

Хорошо, main.o ссылается на foo, но определение не было связано.

Случай 2

Теперь мы пытаемся связать main.o и foo1.o

$ make case2

В foo1.o есть единственное определение foo, поэтому он связывается без ошибок.

Случай 3

Теперь мы пытаемся связать main.o foo1.o и foo2.o

$ make case3
error: multiple definition of 'foo()'

Компоновщик находит два определения foo в foo1.o и foo2.o, поэтому вместо того, чтобы брать случайное, он быстро завершается с ошибкой и не связывает.

Вопросы

Мои вопросы:

  1. Можно ли отметить определение версии foo для foo1.cpp (возможно, с каким-то атрибутом gcc или прагмой) как "слабое"? Это означает, в частности, что я хочу, чтобы поведение case 1 и case 2 оставалось таким же, в то время как поведение case 3 изменяется на успешное связывание без предупреждения или ошибки, а определение foo в foo2.cpp связано (переопределение "слабого" определения в foo1.cpp).

  2. Можно ли проверить во время выполнения (и присвоить логической переменной), была ли связана слабая версия foo или сильная (без очевидного выполнения функции). т.е. реализовать функцию в основном следующим образом.

    template<typename FunctionPtr>
    bool is_weak_linked(FunctionPtr fp);
    

is_weak_linked (foo) должен возвращать true в случае 2 и false в случае 3.


person Andrew Tomazos    schedule 01.02.2012    source источник
comment
Если вы используете google gcc weak attribute, вы получите полезные результаты ...   -  person Ben Jackson    schedule 02.02.2012
comment
Хм? гугл - какое глупое имя - каким бы оно ни было, оно никогда не приживется   -  person Andrew Tomazos    schedule 02.02.2012
comment
Зачем тебе is_weak_linked? Или вообще слабая связь? Почти наверняка есть более элегантное или более портативное решение любой проблемы, которую вы пытаетесь решить.   -  person Managu    schedule 02.02.2012


Ответы (1)


To #1:

void foo() __attribute__((weak));
void foo()
{
    ...
}

К №2: Я так не думаю, только не с учетом статических ссылок, которые вы делаете. Эта информация теряется во время связывания.

Изменить

Вы всегда можете абстрагироваться от №2. Некоторые примеры:

// Foo.h
extern void foo();
extern "C" void default_foo();
extern const bool is_weak_foo_linked;
template <void (*)()> bool is_weak_linked();

// Foo1.cc
extern "C" void default_foo() {...}
void foo() __attribute__((weak, alias("default_foo")));

extern const bool __attribute__((weak)) is_weak_foo_linked=true;
template <> bool is_weak_linked<foo>() __attribute__((weak));
template <> bool is_weak_linked<foo>() {return true;}

// Foo2.cc
void foo() {...}
extern const bool is_weak_foo_linked=false;
template <> bool is_weak_linked<foo>() {return false;}

// main.cc
#include "foo.h"
#include <iostream>
int main() {
    foo();
    std::cout<<is_weak_foo_linked<<std::endl;
    std::cout<<is_weak_linked<foo>()<<std::endl;
    std::cout<<(foo==default_foo)<<std::endl;
}
person Managu    schedule 01.02.2012
comment
Возможно, можно пометить слабую версию каким-то определенным пользователем атрибутом, который сохраняется до времени выполнения? - person Andrew Tomazos; 02.02.2012
comment
@ user1131467: подписи должны быть идентичными, иначе это будет перегрузка и проблем со ссылками не будет. - person Mooing Duck; 02.02.2012
comment
@MooingDuck: ага, я полагаю, это зависит от того, насколько низко ты хочешь пойти. Вы всегда можете сделать что-нибудь действительно неприятное, например, пометить слабую версию обнаруживаемой последовательностью байтов в ее машинном коде. Но при этом мы очень далеко уходим от C ++. - person Managu; 02.02.2012