Подтвердить что ты не робот

G++, требуют ссылки или предупреждения компоновщика для множественной специализации шаблона

Представьте, что у вас есть файл a.h

 #include <iostream>

template<typename T> struct A{  
  int magic;
  A():magic(1234){}
  void f(){std::cout<<"default f"<<magic<<std::endl;}
};


void f(A<int>* a);

то функция f определена в "a.cpp"

  #include "a.h"
void f(A<int>* a){
  a->f();
}

и, наконец, "main.cpp" специализируется на шаблоне, а затем использует f

#include "a.h"
template<> struct A<int>{   
};

int main(){
  A<int> a;
  f(&a);

}

Очевидно, что компилятор использует неспециализированную версию для a.o и специализированную версию для main.o, то есть существуют две разные реализации A. При выполнении f может печатать только мусор /segfault, потому что переданный объект имеет другую структуру от ожидаемой.

Есть ли способ заставить компоновщика предупредить, что есть две версии A?

4b9b3361

Ответ 1

Причина, по которой Gold не предупреждает об этом, заключается в том, что Gold только обнаруживает несоответствия символов (один и тот же символ определяется в нескольких объектных файлах несовместимыми способами), и в этом примере нет такого несоответствия.

Запуск примера под Valgrind вызывает эту ошибку:

valgrind --track-origins=yes ./a.out

==11004== Memcheck, a memory error detector
==11004== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==11004== Using Valgrind-3.8.0.SVN and LibVEX; rerun with -h for copyright info
==11004== Command: ./a.out
==11004== 
==11004== Conditional jump or move depends on uninitialised value(s)
==11004==    at 0x40B6D24: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (in /usr/lib64/libstdc++.so.6.0.16)
==11004==    by 0x40B703C: std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::do_put(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (in /usr/lib64/libstdc++.so.6.0.16)
==11004==    by 0x40C26DE: std::ostream& std::ostream::_M_insert<long>(long) (in /usr/lib64/libstdc++.so.6.0.16)
==11004==    by 0x40094F: A<int>::f() (a.h:6)
==11004==    by 0x4008CB: f(A<int>*) (a.cpp:3)
==11004==    by 0x400977: main (main.cpp:7)
==11004==  Uninitialised value was created by a stack allocation
==11004==    at 0x400964: main (main.cpp:5)

Вы должны получить еще лучший отчет от Address Sanitizer:

Update:

Дело в том, что я хотел бы обнаружить ошибку при связывании времени, а не во время выполнения.

Я понимаю вашу точку зрения, но в настоящее время невозможно, чтобы компилятор (не имеет информации о других единицах перевода) или компоновщик (не имеет информации о связанных типах), чтобы предупредить вас об этом.

Теперь для построения отладки компоновщик может теоретически сделать это, если для каждой функции он также сравнивает информацию об отладке для типов параметров. Я предлагаю подать запрос на функцию для золота в bugzilla.

Ответ 2

Золотой линкер может дать предупреждение с нарушениями -detect-odr

Это работает путем сравнения номера файла и строки для каждого определения шаблона и предупреждения, если они не все одинаковы.

Ответ 3

Я думаю, что ответ "нет", и он останется таким.

Типы имеют только имена, которые видит компоновщик, когда они появляются в функциональных параметрах или аргументах шаблона (возможно, некоторые другие странные вещи). Ваш пример на самом деле является одним из самых простых случаев и обнаруживает, что компоновщик должен работать с ABI, который (по сути) отмечает аргументы шаблона, предоставленные специализацией. Но они не могут этого сделать: вы должны иметь возможность передать указатель на шаблонную структуру, не зная, указывает ли она на специализацию.

Даже тогда вы не можете получить гораздо более радикальные, чем даже тривиальные изменения ABI, это означает, по крайней мере, рассмотрение необходимости перекомпилировать и/или перекомпоновковать каждую библиотеку и исполняемый файл. Если ваши структуры были членами struct trojan { A<int> greeks; }, у вас были бы одинаковые имена типов, и если они никогда не появлялись в качестве функциональных параметров или аргументов шаблона, компоновщик никогда их не увидит, даже если они были разными.

Чтобы получить автоматическое обнаружение, я бы начал с доступного интерфейса OSS С++, такого как clang. Вам понадобятся (нестандартные) правила управления именами, которые будут помечать имена аргументов шаблона-специализации и заставить их генерировать списки боковой полосы всех объявлений шаблонов, на которые он ссылается. Затем напишите отдельный инструмент, который просматривает списки для всех объектов, которые связаны друг с другом, и жалуется, если он находит имя + аргументы, используемые (а не только указанные или объявленные) в одном объекте, который также используется в другом, но из другой специализации.