Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> multiple declaration, Какие есть принципы создания библиотек 
V
    Опции темы
d06osipov
Дата 7.9.2007, 22:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 72
Регистрация: 1.11.2006

Репутация: нет
Всего: нет



Я много раз слышал, что объявление функций и статических данных нужно размещать в .cpp файле, иначе будет multiple definition, что и происходит. Но, например, Boost целиком состоит только из hpp файлов, в которых, следовательно, содержаться и определения функций. Как так получается, что при подключении в двух файлов одной библиотеки Boost, не возникает multiple definition? И вообще, какие существуют принципы написания библиотек, если не хочется делать объектных файлов для библиотеки --- их создание усложняет использование и не позволяет делать оптимизацию. 

Всякие конструкции вида 

Код

#ifndef LIBRARY_INCLUDED
#define LIBRARY_INCLUDED

.....

#endif


не помогают.
PM MAIL   Вверх
bsa
Дата 7.9.2007, 22:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



d06osipov, ты просто не понимаешь, что происходит. У буста, кстати, тоже есть библиотеки, которые надо цеплять (например, program_options, thread и др.). Только некоторые вещи реализованы как встраиваемые (inline), а другие - просто шаблонные. В первом случае компилятор каждый раз вместо вызова функции подставляет ее код, во втором случае он может, как вставить код, так и сделать вызов. Но в любом случае код генерится для каждой реализации шаблона.
Так можно определить встраиваемую функцию:
Код
static inline type myFunction(void *param) {
....
}
Так можно определить шаблон:
Код
template<class T>
T& myFunc(T &param) {
...
}
Оба этих примера можно размещать в заголовочном файле и подключать в любое количество модулей не опасаясь получить ошибки.
PM   Вверх
Damarus
Дата 7.9.2007, 22:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Awaiting Authorisation
Сообщений: 671
Регистрация: 6.5.2006

Репутация: 6
Всего: 29



Цитата(d06osipov @  7.9.2007,  23:01 Найти цитируемый пост)
Но, например, Boost целиком состоит только из hpp файлов, в которых, следовательно, содержаться и определения функций.

boost почти полностью состоит из шаблонов  smile 
PM MAIL ICQ Jabber   Вверх
UnrealMan
Дата 7.9.2007, 22:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 722
Регистрация: 30.3.2006

Репутация: 27
Всего: 32



Цитата(bsa @  7.9.2007,  23:23 Найти цитируемый пост)
Только некоторые вещи реализованы как встраиваемые (inline), а другие - просто шаблонные. В первом случае компилятор каждый раз вместо вызова функции подставляет ее код, во втором случае он может, как вставить код, так и сделать вызов.

В первом случае он тоже может сделать вызов. inline-овость функции в этом плане компилятором может игнорироваться вообще. Кроме того, inline-функциями являются не только функции, объявленные с ключевым словом inline, но и все методы, определённые внутри определения класса.

Главным отличием inline-функций от нешаблонной не-inline в данном случае является то, что определение inline-функции может и должно дублироваться в каждой единице трансляции, где данная функция используется.

Это сообщение отредактировал(а) UnrealMan - 7.9.2007, 22:46
PM MAIL   Вверх
d06osipov
Дата 7.9.2007, 22:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 72
Регистрация: 1.11.2006

Репутация: нет
Всего: нет



Цитата

d06osipov, ты просто не понимаешь, что происходит


Если бы я понимал, что происходит, я бы, наверное, не создавал эту тему. Например, я не понимаю, как компилятор допускает описание одного и того же класса в разных файлах и что происходит, если этот класс описан по-разному, и как это диагностируется, если как-то диагностируется.

Цитата

static inline type myFunction(void *param) 


Значит, inline функции всё-таки, вопреки принципу, ставить inline только там, где это действительно необходимо, рекомендуется применять при разработке библиотек.

Интересный момент --- если инлайновость и игнорируется семантически, то не игнорируется синтаксически (не создаёт multiple definition). 

А на шаблонные, как я только что заметил, компилятор действительно не ругается.
PM MAIL   Вверх
UnrealMan
Дата 7.9.2007, 22:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 722
Регистрация: 30.3.2006

Репутация: 27
Всего: 32



Цитата(d06osipov @  7.9.2007,  23:43 Найти цитируемый пост)
Например, я не понимаю, как компилятор допускает описание одного и того же класса в разных файлах и что происходит, если этот класс описан по-разному, и как это диагностируется, если как-то диагностируется.

Всё это описано в стандарте:

Цитата(ISO/IEC 14882:2003(E) 3.2 One definition rule)

1 No translation unit shall contain more than one definition of any variable, function, class type, enumeration
type or template.

2 An expression is potentially evaluated unless it appears where an integral constant expression is required
(see 5.19), is the operand of the sizeof operator (5.3.3), or is the operand of the typeid operator and
the expression does not designate an lvalue of polymorphic class type (5.2.8). An object or non-overloaded
function is used if its name appears in a potentially-evaluated expression. A virtual member function is
used if it is not pure. An overloaded function is used if it is selected by overload resolution when referred
to from a potentially-evaluated expression. [Note: this covers calls to named functions (5.2.2), operator
overloading (clause 13), user-defined conversions (12.3.2), allocation function for placement new (5.3.4),
as well as non-default initialization (8.5). A copy constructor is used even if the call is actually elided by
the implementation. ] An allocation or deallocation function for a class is used by a new expression appearing
in a potentially-evaluated expression as specified in 5.3.4 and 12.5. A deallocation function for a class
is used by a delete expression appearing in a potentially-evaluated expression as specified in 5.3.5 and 12.5.
A copy-assignment function for a class is used by an implicitly-defined copy-assignment function for
another class as specified in 12.8. A default constructor for a class is used by default initialization as specified
in 8.5. A constructor for a class is used as specified in 8.5. A destructor for a class is used as specified
in 12.4.

3 Every program shall contain exactly one definition of every non-inline function or object that is used in that
program; no diagnostic required. The definition can appear explicitly in the program, it can be found in the
standard or a user-defined library, or (when appropriate) it is implicitly defined (see 12.1, 12.4 and 12.8).
An inline function shall be defined in every translation unit in which it is used.

4 Exactly one definition of a class is required in a translation unit if the class is used in a way that requires the
class type to be complete. [Example: the following complete translation unit is well-formed, even though it
never defines X:

Код

struct X; // declare X as a struct type
struct X* x1; // use X in pointer formation
X* x2; // use X in pointer formation

—end example] [Note: the rules for declarations and expressions describe in which contexts complete class
types are required. A class type T must be complete if:
— an object of type T is defined (3.1, 5.3.4), or
— an lvalue-to-rvalue conversion is applied to an lvalue referring to an object of type T (4.1), or
— an expression is converted (either implicitly or explicitly) to type T (clause 4, 5.2.3, 5.2.7, 5.2.9, 5.4), or
— an expression that is not a null pointer constant, and has type other than void *, is converted to the
type pointer to T or reference to T using an implicit conversion (clause 4), a dynamic_cast (5.2.7) or
a static_cast (5.2.9), or
— a class member access operator is applied to an expression of type T (5.2.5), or
— the typeid operator (5.2.8) or the sizeof operator (5.3.3) is applied to an operand of type T, or
— a function with a return type or argument type of type T is defined (3.1) or called (5.2.2), or
— an lvalue of type T is assigned to (5.17). ]

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 specialization
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 requirements.
Given such an entity named D defined in more than one translation unit, then
— each definition of D shall consist of the same sequence of tokens; and
— in each definition of D, corresponding names, looked up according to 3.4, shall refer to an entity defined
within the definition of D, or shall refer to the same entity, after overload resolution (13.3) and after
matching of partial template specialization (14.8.3), except that a name can refer to a const object
with internal or no linkage if the object has the same integral or enumeration type in all definitions of D,
and the object is initialized with a constant expression (5.19), and the value (but not the address) of the
object is used, and the object has the same value in all definitions of D; and
— in each definition of D, the overloaded operators referred to, the implicit calls to conversion functions,
constructors, operator new functions and operator delete functions, shall refer to the same function, or to
a function defined within the definition of D; and
— in each definition of D, a default argument used by an (implicit or explicit) function call is treated as if
its token sequence were present in the definition of D; that is, the default argument is subject to the three
requirements described above (and, if the default argument has sub-expressions with default arguments,
this requirement applies recursively).25)
— if D is a class with an implicitly-declared constructor (12.1), it is as if the constructor was implicitly
defined in every translation unit where it is used, and the implicit definition in every translation unit
shall call the same constructor for a base class or a class member of D. [Example:

Код

// translation unit 1:
struct X {
    X(int);
    X(int, int);
};
X::X(int = 0) { }
class D: public X { };
D d2; // X(int) called by D()
// translation unit 2:
struct X {
    X(int);
    X(int, int);
};
X::X(int = 0, int = 0) { }
class D: public X { }; // X(int, int) called by D();
// D()’s implicit definition
// violates the ODR

—end example] If D is a template, and is defined in more than one translation unit, then the last four
requirements from the list above shall apply to names from the template’s enclosing scope used in the
template definition (14.6.3), and also to dependent names at the point of instantiation (14.6.2). If the
definitions of D satisfy all these requirements, then the program shall behave as if there were a single
definition of D. If the definitions of D do not satisfy these requirements, then the behavior is undefined.

Копипаст не очень качественный получился, лучше в оригинал заглянуть.


PM MAIL   Вверх
bsa
Дата 7.9.2007, 23:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



Цитата(d06osipov @ 7.9.2007,  22:43)
Например, я не понимаю, как компилятор допускает описание одного и того же класса в разных файлах и что происходит, если этот класс описан по-разному, и как это диагностируется, если как-то диагностируется.

Очень просто. Каждый *.cpp файл превращается в объектный файл. У этого объектного файла есть таблица экспортируемых символов - нестатических функции и переменных, определенных в этом *.cpp файле. Когда линковщик начинает объединять все объектные файлы он читает эти таблицы всех файлов и, в случае обнаружения двух одинаковых символов, ругается.
Так что дело тут не столько в inline, сколько в static. Не забывай, компилятор совсем не обязан выполнять директиву inline  smile 
PM   Вверх
d06osipov
Дата 7.9.2007, 23:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 72
Регистрация: 1.11.2006

Репутация: нет
Всего: нет



Цитата(bsa @  7.9.2007,  23:22 Найти цитируемый пост)
Так что дело тут не столько в inline, сколько в static. Не забывай, компилятор совсем не обязан выполнять директиву


Здесь уже писалось, что компилятор может семантически не воспринимать inline, но дублироваться в каждом модуле такая функция обязана. 

Цитата(bsa @  7.9.2007,  23:22 Найти цитируемый пост)
он читает эти таблицы всех файлов и, в случае обнаружения двух одинаковых символов, ругается.

Эксперементально проверено на нескольких компиляторах: разрешаются в разных единицах трансляции классы с одинаковыми именами. Это же подтверждается Стандартом. 

PM MAIL   Вверх
archimed7592
Дата 8.9.2007, 06:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Архимед
****


Профиль
Группа: Завсегдатай
Сообщений: 2531
Регистрация: 12.6.2004
Где: Moscow

Репутация: 58
Всего: 93



Цитата(d06osipov @  7.9.2007,  23:43 Найти цитируемый пост)
Эксперементально проверено на нескольких компиляторах: разрешаются в разных единицах трансляции классы с одинаковыми именами. Это же подтверждается Стандартом. 

Хочешь сказать эти два модуля можно будет скомпоновать smile?
Код

// module1.cpp
class C
{
public:
    C();
    void f();
};
C::C()
{ }

void C::f()
{ }

// module2.cpp
class C
{
public:
    C(int);
    void f();
};
C::C(int)
{ }

void C::f()
{ }



--------------------
If you have an apple and I have an apple and we exchange apples then you and I will still each have one apple. But if you have an idea and I have an idea and we exchange these ideas, then each of us will have two ideas.
© George Bernard Shaw
PM Jabber   Вверх
bsa
Дата 8.9.2007, 09:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



Цитата(d06osipov @ 7.9.2007,  23:43)
Здесь уже писалось, что компилятор может семантически не воспринимать inline, но дублироваться в каждом модуле такая функция обязана.

А ты возьми и попробуй собрать проект с полной оптимизацией (когда inline скорее всего встраивается) и без оптимизации вообще (когда не встраивается). И ты поймешь, зачем нужен static.
PM   Вверх
d06osipov
Дата 12.9.2007, 18:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 72
Регистрация: 1.11.2006

Репутация: нет
Всего: нет



Цитата(archimed7592 @  8.9.2007,  06:27 Найти цитируемый пост)
Хочешь сказать эти два модуля можно будет скомпоновать


А здесь определена два раза функция с именем C::f, чего нельзя.
PM MAIL   Вверх
archimed7592
Дата 12.9.2007, 18:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Архимед
****


Профиль
Группа: Завсегдатай
Сообщений: 2531
Регистрация: 12.6.2004
Где: Moscow

Репутация: 58
Всего: 93



Цитата(d06osipov @  12.9.2007,  18:17 Найти цитируемый пост)
А здесь определена два раза функция с именем C::f, чего нельзя. 

А если не определять функциональность класса, то от него не особо много пользы получается...


--------------------
If you have an apple and I have an apple and we exchange apples then you and I will still each have one apple. But if you have an idea and I have an idea and we exchange these ideas, then each of us will have two ideas.
© George Bernard Shaw
PM Jabber   Вверх
Earnest
Дата 13.9.2007, 19:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 53
Всего: 183



Цитата(d06osipov @  8.9.2007,  00:43 Найти цитируемый пост)
Эксперементально проверено на нескольких компиляторах: разрешаются в разных единицах трансляции классы с одинаковыми именами. Это же подтверждается Стандартом. 

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



--------------------
...
PM   Вверх
archimed7592
Дата 13.9.2007, 20:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Архимед
****


Профиль
Группа: Завсегдатай
Сообщений: 2531
Регистрация: 12.6.2004
Где: Moscow

Репутация: 58
Всего: 93



Цитата(Earnest @  13.9.2007,  19:38 Найти цитируемый пост)
Так что лучше этого всячески избегать: использовать, скажем, пространства имен...

А если с одним именем класса код получается элегантнее и лаконичнее, то безымянное пространство имён - очень удобно.

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


--------------------
If you have an apple and I have an apple and we exchange apples then you and I will still each have one apple. But if you have an idea and I have an idea and we exchange these ideas, then each of us will have two ideas.
© George Bernard Shaw
PM Jabber   Вверх
Earnest
Дата 14.9.2007, 07:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 53
Всего: 183



Цитата(archimed7592 @  13.9.2007,  21:11 Найти цитируемый пост)
Кстати, я вообще не понимаю, зачем экспортировать из модуля какие-либо имена, относящиеся к деталям реализации.

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



--------------------
...
PM   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0664 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.