LINUX.ORG.RU
Ответ на: комментарий от guyvernk

разглядывая код некоторых опенсорсных проектов хочется разбить себе лицо

Странная логика. То, что некоторые программы на $langname написаны плохо, не значит, что на этом же $langname нельзя писать хорошо. А так-то, программу на фортране можно написать на любом языке..

Manhunt ★★★★★
()
Последнее исправление: Manhunt (всего исправлений: 1)
Ответ на: комментарий от rumgot
/*
$ cat a.c
*/

#include <stdio.h>

struct S {
  char* data;
};

extern void f(struct S* s);

int main() {
  struct S s;
  f(&s);
  printf("%s\n", s.data);
}

b.c

/*
$ cat b.c
*/
struct S {
  //char* data;
  int data;
};

void f(struct S* s) {
  //s->data = "hello";
  s->data = 10;
}
$ gcc a.c b.c -o test_odr && ./test_odr
Segmentation fault (core dumped)
anonymous
()
Ответ на: комментарий от blex

static, потому что с дефолтными настройками видимости символов если вы подключите этот файл с двух местах то компилятор вам скажет «дядя, этот символ уже есть в другом месте».

PPP328 ★★★★★
()

Но у меня в h есть логика. Это очень плохо?

Это чудовищно! Это жутко затрудняет взаимодействие с программами на других языках

Но также это штатное место для шаблонов, например

ahdenchik
()
Последнее исправление: ahdenchik (всего исправлений: 1)

Если сомневаешься и оно само так вышло, значит ты ещё не готов к тому, чтобы мотивировано добавлять логику/реализацию в заголовочных файлах. Следуй рекомендации о разделении интерфейса и реализации.

P.S. Когда дорастешь до сеньора пограмиста, то узнаешь что можно создавать целые фреймворки чисто на шаблонах в hpp-файлах.

necromant ★★
()
Ответ на: комментарий от rumgot
// a.h
#pragma once

struct A{
#ifdef XXX
	int x = -1;
#endif //XXX
	int y = -2;
	void f();
};
// a.cpp
#include <stdio.h>
#include "a.h"

void A::f(){ printf("y=%i\n", y); }
#include "a.h"

int main(){
	A a;
	a.y = 1;
	a.f();
	return 0;
}
$ g++ -Wall -DXXX -c a.cpp
$ g++ -Wall -c b.cpp
$ g++ -o rumgot a.o b.o
$ ./rumgot 
y=-2088792064

$ g++ -Wall -c a.cpp
$ g++ -Wall -DXXX -c b.cpp
$ g++ -o rumgot a.o b.o
$ ./rumgot 
y=-1

AntonI ★★★★★
()
Последнее исправление: AntonI (всего исправлений: 1)
Ответ на: комментарий от Manhunt

когда в разных compilation units один и тот же тип имеет разный размер

Размер может и сохранятся, достаточно что бы смещения внутри структуры поплыли

struct A{
#ifdef XXX
   int x;  // вчера ты не дал мне сигарету?
#endif
   int y;
#ifndef XXX
   int x;  // я еще Makefile подшаманил, приятной отладки, бро!
#endif
};
AntonI ★★★★★
()
Ответ на: комментарий от rumgot

Тут одинаковые типы. И это пример нарушения ODR.

Линкер тупой. Он смотрит - есть объект, в нем поле x имеет тип Т и смещение off, так к нему и обращаемся. Ликёр наивно считает что во всех обьектниках эта информация одинакова. Если информация разная то линкер ни за что не отвечает - можно попасть за пределы объекта (как в варианте 1) или в другое поле (как в варианте 2).

AntonI ★★★★★
()
Ответ на: комментарий от rumgot

Молодец, верно отвечаешь, типы здесь действительно разные. И уж тем более, это не соответствует теме писать код в .h или в .c.

Проблема здесь в том, что линкер эти типы никак проверять не может - типов нет на уровне линкера. Вопреки утверждениям выше о том, что линкер смотрит в типы: мог бы он смотреть в типы - он бы выдал ошибку о несоответствии.

Поэтому нужно руками гарантировать эквивалентность того, что передаётся между разными tu. Вот тот самый odr частично пытается этому способствовать, выдавая ошибку на переопределение идентификатора в рамках одного tu. Т. е. сам odr существует только в рамках tu, между разными tu odr уже нет.

anonymous
()
Ответ на: комментарий от anonymous

Вопреки утверждениям выше о том, что линкер смотрит в типы: мог бы он смотреть в типы - он бы выдал ошибку о несоответствии.

Линкер с LTO ругается на несоответствие типов.

C++ ABI позволяет сохранять типы, частично.

ODR также касается определению имён между единицами трансляции. Что может быть несколько определений у структур/классов в единицах трансляции, но определения должны совпадать.

anonymous
()
Ответ на: комментарий от anonymous

Линкер с LTO ругается на несоответствие типов.

Выше нигде нет lto. Как и нет никакого смысла его использовать.

C++ ABI позволяет сохранять типы, частично.

Не позволяет. Кстати, частично - значит уже не позволяет в целом.

ODR также касается определению имён между единицами трансляции. Что может быть несколько определений у структур/классов в единицах трансляции, но определения должны совпадать.

Не касается. Нет, совпадать безусловно они не должны. Я написал, при каких условиях нужно совпадение.

anonymous
()
Ответ на: комментарий от anonymous

Я описал чем является odr в реальности(в контексте темы). Какие-то пункты ничего не значат. По крайней мере, ты должен из этих пунктов вывести обоснование того, о чём пытаешься говорить.

anonymous
()

Это очень плохо?

В большинстве случаев — да, особенно если у тебя большой многомодульный проект. Но есть исключения, например, для inline-методов.

hobbit ★★★★★
()

h - прототип Cpp - реализация. Но у меня в h есть логика. Это очень плохо?

У такого подхода есть только один плюс: компилятор сможет инлайнить код из .h по месту использования. Но с этим, как и с любой оптимизацией, нужна осторожность, так как инлайнинг раздувает размер бинарников, а поможет ли он что-нибудь ускорить, зависит от обстоятельств. К тому же если использовать LTO, то ничего выносить в заголовки уже не требуется, т.к. компилятор видит код всего бинарника сразу.

А минусов полно, с некоторыми можно бороться (например, разделить прототипы и реализацию на header.h и header_impl.h), с некоторыми нет (при любом изменении в «логике» придётся перекомпиливать всё, куда инклудится заголовок)

annulen ★★★★★
()
Последнее исправление: annulen (всего исправлений: 1)