компилятору жестоко рвёт крышу :(
От: nen777w  
Дата: 21.07.11 12:18
Оценка:
Такая проблема.
есть совершенно два разных cpp файла в которых в namespace определена одна и та же структура:

//a.cpp
namespace A
{
   struct field_
   { 
      field_() : key(0) {}
      unsigned int key;
   };
}



//b.cpp
namespace A
{
   struct field_
   { 
      field_() : key(0) {}
      unsigned int key;
      std::string  str;
   };
}


в файлe a.cpp
есть метод:

//a.cpp
void foo()
{
A::field_ test;
}

фигня в том что тут вызывается конструктор структуры определённой в b.cpp
ребилд не помогает.

sizeof() в рантайме и в watch тоже не совпадает.

попытаюсь дать минимальный проект где этот эффект повторяетя попозже. но мож кто сталкивался с таким?
компилятор msvc 9.0
Re: компилятору жестоко рвёт крышу :(
От: uzhas Ниоткуда  
Дата: 21.07.11 12:24
Оценка: 1 (1) +3
Здравствуйте, nen777w, Вы писали:

N>Такая проблема.

N>есть совершенно два разных cpp файла в которых в namespace определена одна и та же структура:
у вас структуры разные определяются, хотя имена совпадают, имеют внешнее связывание
получаем UB
вам нужно убрать совпадение имен (у вас клеш)
чего вы ожидали от этого кода?
Re: компилятору жестоко рвёт крышу :(
От: Alexey F  
Дата: 21.07.11 12:28
Оценка: +2 -1
Здравствуйте, nen777w, Вы писали:

N>есть совершенно два разных cpp файла в которых в namespace определена одна и та же структура:

Нарушение ODR.
Если имена менять не хочется, преодолеть это поможет дополнительное оборачивание их в анонимное пространство имён.
Re[2]: компилятору жестоко рвёт крышу :(
От: dilmah США  
Дата: 21.07.11 12:50
Оценка:
N>>есть совершенно два разных cpp файла в которых в namespace определена одна и та же структура:
AF>Нарушение ODR.

и причем здесь ODR?
Re[2]: компилятору жестоко рвёт крышу :(
От: nen777w  
Дата: 21.07.11 12:52
Оценка:
Здравствуйте, uzhas, Вы писали:

U>Здравствуйте, nen777w, Вы писали:


N>>Такая проблема.

N>>есть совершенно два разных cpp файла в которых в namespace определена одна и та же структура:
U>у вас структуры разные определяются, хотя имена совпадают, имеют внешнее связывание
U>получаем UB
U>вам нужно убрать совпадение имен (у вас клеш)
U>чего вы ожидали от этого кода?

погодите не понял.
структуры то определяются в cpp файлах. наружу они никак не видны. это локальные типы конкретного модуля компиляции. разве не так?
Re[3]: компилятору жестоко рвёт крышу :(
От: uzhas Ниоткуда  
Дата: 21.07.11 12:57
Оценка: 6 (1) +2
Здравствуйте, nen777w, Вы писали:

N>структуры то определяются в cpp файлах. наружу они никак не видны. это локальные типы конкретного модуля компиляции. разве не так?

они видны наружу, поэтому конфликтуют
поместите их в анонимный неймспейс, тогда клеш пропадет
Re[3]: компилятору жестоко рвёт крышу :(
От: Alexey F  
Дата: 21.07.11 12:58
Оценка: +2
Здравствуйте, dilmah, Вы писали:

D>и причем здесь ODR?

Как причём?
Стандарт, 3.2.5:

// больше одного, но
There can be more than one definition of a class type (clause 9), enumeration type (7.2), inline function
with external linkage (7.1.2), class template (clause 14), non-static function template (14.5.5), static data
member of a class template (14.5.1.3), member function of a class template (14.5.1.1), or template special-
ization for which some template parameters are not specified (14.7, 14.5.4) in a program provided that each
definition appears in a different translation unit, and provided the definitions satisfy the following require-
ments. Given such an entity named D defined in more than one translation unit, then

// ---v
— each definition of D shall consist of the same sequence of tokens; and
...

Re[4]: компилятору жестоко рвёт крышу :(
От: blackhearted Украина  
Дата: 21.07.11 12:59
Оценка:
Здравствуйте, uzhas, Вы писали:

U>Здравствуйте, nen777w, Вы писали:


N>>структуры то определяются в cpp файлах. наружу они никак не видны. это локальные типы конкретного модуля компиляции. разве не так?

U>они видны наружу, поэтому конфликтуют
U>поместите их в анонимный неймспейс, тогда клеш пропадет
static не поможет?
Re[3]: компилятору жестоко рвёт крышу :(
От: uzhas Ниоткуда  
Дата: 21.07.11 13:00
Оценка: +2
Здравствуйте, dilmah, Вы писали:

D>и причем здесь ODR?

в данном случае мы имеем определение двух одинаковых структур (имена типов совпадают), которые различаются. это нарушает ODR и приводит к неопределенному поведению (которое не обязано диагностироваться)
если бы мы одинаково определили эти структуры в двух .cpp, то нарушения ODR не было бы
Re[5]: компилятору жестоко рвёт крышу :(
От: uzhas Ниоткуда  
Дата: 21.07.11 13:03
Оценка:
Здравствуйте, blackhearted, Вы писали:

B>static не поможет?

а куда выхотите его приписать?
static namespace? static struct? в этих случаях не поможет
Re[6]: компилятору жестоко рвёт крышу :(
От: blackhearted Украина  
Дата: 21.07.11 13:10
Оценка:
Здравствуйте, uzhas, Вы писали:

U>Здравствуйте, blackhearted, Вы писали:


B>>static не поможет?

U>а куда выхотите его приписать?
U>static namespace? static struct? в этих случаях не поможет

точно это же definition... признаю совю неправоту
Re[4]: компилятору жестоко рвёт крышу :(
От: nen777w  
Дата: 21.07.11 13:16
Оценка:
Здравствуйте, uzhas, Вы писали:

U>Здравствуйте, nen777w, Вы писали:


N>>структуры то определяются в cpp файлах. наружу они никак не видны. это локальные типы конкретного модуля компиляции. разве не так?

U>они видны наружу, поэтому конфликтуют
U>поместите их в анонимный неймспейс, тогда клеш пропадет

вот блин... Я не знал об этом интересном поведении линкера.
Как это правильно называется что бы "ликбез почитать"?
Re[5]: компилятору жестоко рвёт крышу :(
От: uzhas Ниоткуда  
Дата: 21.07.11 13:22
Оценка: 7 (2)
Здравствуйте, nen777w, Вы писали:
N>Как это правильно называется что бы "ликбез почитать"?
external linkage
http://stackoverflow.com/questions/1358400/what-is-external-linkage-and-internal-linkage-in-c
Re: компилятору жестоко рвёт крышу :(
От: PimpDaddy Россия  
Дата: 21.07.11 13:33
Оценка: 6 (1) -2
Так как конструктор объявляется в теле класса, он неявно становится inline. Эксемпляр конструктора будет в каждой единицы трансляции, где используется. В некоторых случаях функция будет встроена, в других — нет. Определения одной и той же функции должны быть одинаковы, иначе — undefined behaviour. Visual Studio, похоже, на этапе линковки молча выбирает одну из реализаций.

Сцылка1, Сцылка2.
Re[3]: компилятору жестоко рвёт крышу :(
От: jazzer Россия Skype: enerjazzer
Дата: 22.07.11 00:41
Оценка:
Здравствуйте, nen777w, Вы писали:

N>структуры то определяются в cpp файлах. наружу они никак не видны. это локальные типы конкретного модуля компиляции. разве не так?


Что, по-твоему, означает выражение "видны наружу"?

Прадставь, что у нас есть хедер с forward declaration, который подключается к каждому твоему cpp-файлу:
namespace A
{
   struct field_;
}


Как ты думаешь, с таким хедером ты имеешь право реализовывать структуры по-разному?
А теперь подумай, это чем-нть отличается от твоего кода, с учетом того, что содержимое хедеров просто включается препроцессором в файл как текст?
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[4]: компилятору жестоко рвёт крышу :(
От: nen777w  
Дата: 22.07.11 10:40
Оценка:
J>Как ты думаешь, с таким хедером ты имеешь право реализовывать структуры по-разному?
J>А теперь подумай, это чем-нть отличается от твоего кода, с учетом того, что содержимое хедеров просто включается препроцессором в файл как текст?

да, да это Я только потом подумал по сути после препроцессинга собирается один огромный модуль, который передается на вход компилятору, который и подготавливает obj модули с информацией для линкера, и там уже для него нет различия внутренний/наружный.
но warning наверно мог бы и показать, у меня не показывал.
кстати это ведь не правильно (warning level правда дефолтный), но ведь это такая скрытая подляна получается?
Re[5]: компилятору жестоко рвёт крышу :(
От: jazzer Россия Skype: enerjazzer
Дата: 22.07.11 11:22
Оценка: 7 (2)
Здравствуйте, nen777w, Вы писали:

J>>Как ты думаешь, с таким хедером ты имеешь право реализовывать структуры по-разному?

J>>А теперь подумай, это чем-нть отличается от твоего кода, с учетом того, что содержимое хедеров просто включается препроцессором в файл как текст?

N>да, да это Я только потом подумал по сути после препроцессинга собирается один огромный модуль, который передается на вход компилятору, который и подготавливает obj модули с информацией для линкера, и там уже для него нет различия внутренний/наружный.

Нет, почему же, там различие есть как раз. Все, что с external linkage, будет наружным, все, что с internal — видно не будет.

N>но warning наверно мог бы и показать, у меня не показывал.

N>кстати это ведь не правильно (warning level правда дефолтный), но ведь это такая скрытая подляна получается?

Ну это ты линкеру предлагаешь нехило поработать так... Т.е. вот есть класс, у него с десяток методов. И линкер долджен определить, что у него этот десяток методов, потом каждый с каждым сравнить насквозь через все объектники, которые он линкует (а их может быть несколько тысяч), и посмотреть, все ли одинаково реализованы (а оно может и не быть одинаково, например, регистры по-другому легли или NOP-ов насовали для выравнивания в одном месте и не насовали в другом). Ведь стандарт не требует, чтобы из одного и того же кода всегда получался один и тот же асм. Не говоря уже о случаях встраивания. Причем просто сравнить каждый с каждым недостаточно, нужно ловить отсутствующие вещи из-за перегрузки и прочих радостей.
Или вот еще пример:
// a.cpp
struct Rect
{
  int l,r,t,b;
  int area() const;
};
int Rect::area() const { return (r-l)*(b-t); }

// b.cpp
struct Rect
{
  int l,r,w,h;
  int area() const;
};
//int Rect::area() const { return w*h; }

Обрати внимание, на уровне информации, доступной линкеру, и там, и там 4 инта. И там объявлен Rect::area с одинаковой сигнатурой. Только во втором файле ты забыл ее определить. Ну и что делать линкеру? По сигнатурам все хорошо, можно брать реализацию из a.cpp, которая делать абсолютно не то. А даже если и не забыл определить — может, они одинаковые на самом деле, просто компилятор по-разному к ним отнёсся?

Так что заведи себе правило — всё, что пишется внутри .cpp для его внутренних нужд: хелперы, функторы и т.п. — всё всегда должно лежать в анонимном пространстве имен. Вне этого пространства должны лежать только определения того, что объявлено в хедерах, и функция main. Остальное — прятать.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re: компилятору жестоко рвёт крышу :(
От: IROV..  
Дата: 22.07.11 11:41
Оценка:
Здравствуйте, nen777w, Вы писали:

Ответ дан выше, есть еще похожий баг, когда конструкторы срабатывают коректно, а вот из за того что у них есть виртуальные деструкторы, начинается сказка при удалении, багу искали пару дней, потом я нечайно ее заметил

Вопрос такой, разве тяжело давать варнинг/еррор в линкере когда он находит два определения?
я не волшебник, я только учусь!
Re[4]: компилятору жестоко рвёт крышу :(
От: nen777w  
Дата: 22.07.11 12:00
Оценка:
Здравствуйте, uzhas, Вы писали:

U>Здравствуйте, nen777w, Вы писали:


N>>структуры то определяются в cpp файлах. наружу они никак не видны. это локальные типы конкретного модуля компиляции. разве не так?

U>они видны наружу, поэтому конфликтуют
U>поместите их в анонимный неймспейс, тогда клеш пропадет

гм... а как тогда указать компилятору что нужен именно этот конкретный тип.
Я имею в виду такое:


namespace
{
   struct S
   {
      ....
   }
}

class impl : public impl_t<S> {}; <-- Я так понимаю здесь возникает та же проблема?


Решение поместить эту строчку в этот же анонимный неймспейс?
Re[5]: компилятору жестоко рвёт крышу :(
От: Alexey F  
Дата: 22.07.11 12:08
Оценка:
Здравствуйте, nen777w, Вы писали:

N>гм... а как тогда указать компилятору что нужен именно этот конкретный тип.

N>Я имею в виду такое:

N>
N>namespace
N>{
N>   struct S
N>   {
N>      ....
N>   }
N>}

N>class impl : public impl_t<S> {}; <-- Я так понимаю здесь возникает та же проблема?
N>


N>Решение поместить эту строчку в этот же анонимный неймспейс?

Зачем? S, упомянутый выше в impl_t<S> и будет из этого анонимного namespace. Анонимный namespace автоматом раскрывается как после using'а именованного.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.