static vs unnamed namespace
От: maguadron  
Дата: 14.08.06 17:27
Оценка:
vc7.1

test1.cpp
#include "test.h"

int main()
{
    extern void bar();
    bar();
    foo();
    return 0;
}


test2.cpp
#include "test.h"

void bar()
{
    foo();
}


первый вариант:
test.h
static void foo()
{
    DWORD sign = 0xC0DEC0DE;
    printf("%08X\n", sign);
}

в бинарнике получаем две переменные sign и две функции foo() (одна заинлайнена)

второй вариант:
test.h
namespace
{
    void foo()
    {
        DWORD sign = 0xC0DEC0DE;
        printf("%08X\n", sign);
    }
}

в бинарнике получаем одну переменную sign и одну функцию foo()

получается используя unnamed namespace теперь можно преспокойно define любые функции в *.h instead of *.cpp?
где можно почитать про данное поведение?
Re: static vs unnamed namespace
От: shank  
Дата: 14.08.06 17:48
Оценка:
Здравствуйте, maguadron, Вы писали:

M>получается используя unnamed namespace теперь можно преспокойно define любые функции в *.h instead of *.cpp?

M>где можно почитать про данное поведение?

Насколько я помню unnamed namespaces нельзя помещать в *.h файлы...
Как насчет такого варианта
inline void foo()
{
    DWORD sign = 0xC0DEC0DE;
    printf("%08X\n", sign);
}
Re[2]: static vs unnamed namespace
От: maguadron  
Дата: 14.08.06 18:01
Оценка:
S>Здравствуйте, maguadron, Вы писали:
M>>получается используя unnamed namespace теперь можно преспокойно define любые функции в *.h instead of *.cpp?
M>>где можно почитать про данное поведение?
S>Насколько я помню unnamed namespaces нельзя помещать в *.h файлы...
почему нельзя если всё замечательно работает?

S>Как насчет такого варианта

S>
S>inline void foo()
S>{
S>    DWORD sign = 0xC0DEC0DE;
S>    printf("%08X\n", sign);
S>}
S>

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

просто не хочется держать часто используемые сниппеты в *.h и *.cpp файлах, хочется всё держать в одном большом utils.h, например так:
namespace
{
    namespace utils
    {
        void someusefulfunc()
        {
        }

        class someusefulclass
        {
            void someusefulmethod()
            {
            }
        };
    }
}
Re[3]: static vs unnamed namespace
От: shank  
Дата: 14.08.06 18:16
Оценка:
Здравствуйте, maguadron, Вы писали:

S>>Насколько я помню unnamed namespaces нельзя помещать в *.h файлы...

M>почему нельзя если всё замечательно работает?
Нарушение ODR. Страуструп, параграф 9.2.1, стр. 246.

M>мне нужно чтобы была только одна копия функции, inline размажет функции в каждый объектник

M>просто не хочется держать часто используемые сниппеты в *.h и *.cpp файлах, хочется всё держать в одном большом utils.h, например так:
Re: static vs unnamed namespace
От: MuTPu4  
Дата: 14.08.06 19:50
Оценка:
M>vc7.1

M>первый вариант:

M>...
M>в бинарнике получаем две переменные sign и две функции foo() (одна заинлайнена)
Не совсем ясно, что означает появление переменной sign в бинарнике, т.к. она не имеет связывания. Если же речь идет о числовом литерале 0xC0DEC0DE, то понятие связывания к нему неприменимо и его представление определяется реализацией, на моей VC 7.1 константа подставляется непосредственно в код каждой из функций foo.

M>второй вариант:

M>...
M>в бинарнике получаем одну переменную sign и одну функцию foo()
Это странное поведение, в asm-листинге и объектных файлах я получил по 2 независимых экземпляра foo на доступных мне компиляторах, в том числе на VC 7.1, как и ожидалось. В имя foo после декорирования тем или иным образом включается уникальный идентификатор единицы трасляции. Возможно, линкер оказался достаточно умным, чтобы выкинуть дублирующиеся определения, пока в коде нет взятия адреса.

M>получается используя unnamed namespace теперь можно преспокойно define любые функции в *.h instead of *.cpp?

Во всяком случае, это не будет прямым нарушением ODR.
Re[3]: static vs unnamed namespace
От: MuTPu4  
Дата: 14.08.06 19:50
Оценка:
M>мне нужно чтобы была только одна копия функции, inline размажет функции в каждый объектник
C unnamed namespace будет то же самое. Чем не устраивает стандартная практика размещать в заголовочных файлах только объявления функций?
Re[4]: static vs unnamed namespace
От: MuTPu4  
Дата: 14.08.06 19:50
Оценка:
Здравствуйте, shank, Вы писали:

S>Нарушение ODR. Страуструп, параграф 9.2.1, стр. 246.

Нарушения ODR я здесь пока что не вижу. В разных единицах трансляции члены unnamed namespace (7.3.1.1) являются независимыми сущностями. Комментировать Страуструпа не возьмусь, но использование неименованных пространств имен в заголовках, на мой взгляд, вполне типично (например, boost/bind/placeholders.hpp).
Re[4]: static vs unnamed namespace
От: a-lex Россия  
Дата: 14.08.06 19:55
Оценка: 3 (1)
Здравствуйте, shank, Вы писали:

[]

S>>>Насколько я помню unnamed namespaces нельзя помещать в *.h файлы...

M>>почему нельзя если всё замечательно работает?
S>Нарушение ODR. Страуструп, параграф 9.2.1, стр. 246.

[]

Не напомните содержание этого параграфа? Книга сейчас недоступна.

Непонятно, откуда вдруг возьмется нарушение ODR. Смысл unnamed namespace как раз и состоит в том, что все находящиеся в нем сущности становятся уникальными по отношению к другим единицам трансляции.

To maguadron:
То, что во втором варианте получилась одна функция, это совпадение (точнее -- особенность линкера). Семантически там две разные функции: ::<unnamed_namespace_for_test1.c>::foo() и ::<unnamed_namespace_for_test2.c>::foo(). При смене компилятора (или даже опций компиляции) может оказаться и физически две функции.

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

Впрочем, это справедливо и для варианта 1.

Корректный способ решения, на мой взгляд -- поместить функцию в именованное пространство имен (или в global scope) и объявить ее inline. Современные компиляторы все равно сами решают, встраивать функцию или нет (даже если она и не объявлена inline), а вот проблемы с ODR и статическими переменными внутри функций будут сняты.
Re[5]: static vs unnamed namespace
От: shank  
Дата: 14.08.06 20:56
Оценка:
Здравствуйте, MuTPu4 и a-lex.
Да, про нарушение ODR это я чушь спорол.
У Страуструпа написано

...заголовочный файл никогда не должен содержать:
— определение обычных функций
— определение данных
— определение агрегатов
неименованные пространства имен
— экспортируемые определения шаблонов

Непонятно, тогда, что в этом списке делают "неименованные пространства имен" и почему нет, например, по аналогии, статических переменных, функций?
Re[5]: static vs unnamed namespace
От: shank  
Дата: 14.08.06 21:32
Оценка:
Здравствуйте, MuTPu4, Вы писали:

MTP>Нарушения ODR я здесь пока что не вижу. В разных единицах трансляции члены unnamed namespace (7.3.1.1) являются независимыми сущностями. Комментировать Страуструпа не возьмусь, но использование неименованных пространств имен в заголовках, на мой взгляд, вполне типично (например, boost/bind/placeholders.hpp).


Почему "вполне типично"? Что это дает? Не вижу смысла использовать unnamed namespaces в заголовках.
В boost::bind, возможно, сделали так, чтобы оставить всю реализацию в *.hpp файлах, или для переносимости, или чтобы не писать boost::_1, или еще для чего-нибудь.
Re[6]: static vs unnamed namespace
От: MuTPu4  
Дата: 14.08.06 22:04
Оценка:
Здравствуйте, shank, Вы писали:

S>Почему "вполне типично"? Что это дает? Не вижу смысла использовать unnamed namespaces в заголовках.

S>В boost::bind, возможно, сделали так, чтобы оставить всю реализацию в *.hpp файлах, или для переносимости, или чтобы не писать boost::_1, или еще для чего-нибудь.
Вообще, анонимные пространства имен — это довольно низкоуровневый языковой механизм, который позволяет выразить различные концепции, в том числе и в контексте заголовочных файлов. Под типичным я имел в виду случай, когда автор заголовка хочет предоставить некоторое глобальное имя своим клиентам (например, из соображений удобства использования), но, ествественно, хочет свести риск нарушения ODR в случае конфликта имен к минимуму (еще пример — "boost/mpl/alias.hpp"). Если речь идет о typedef или namespace alias, то нет необходимости волноваться о дублировании кода или данных.
Re[5]: static vs unnamed namespace
От: maguadron  
Дата: 15.08.06 06:24
Оценка:
AL>Корректный способ решения, на мой взгляд -- поместить функцию в именованное пространство имен (или в global scope) и объявить ее inline. Современные компиляторы все равно сами решают, встраивать функцию или нет (даже если она и не объявлена inline), а вот проблемы с ODR и статическими переменными внутри функций будут сняты.

правильно ли я понимаю ситуацию?, если скажу что (под именем функции понимаю декорированное имя в котором закодированы типа параметров (and return value?)):

foo() функция видима снаружи объектника если будут два объектника с одинаковыми именами функций то у линкера возникнет конфликт какой из экземпляров функции использовать (согласно ODR)

inline foo() функция видима снаружи объектника но конфликта не возникает так как линкеру указано брать первый попавшийся экземпляр (того же эффекта можно добиться используя __declspec(selectany))

static foo() функция видима только внутри объектника, конфликта естественно не возникает (разве только если будут две функции с одинаковым именем внутри объектника, но это должно отсеятся ещё на уровне компайлера)

какие ещё могут быть варианты?
Re[2]: static vs unnamed namespace
От: maguadron  
Дата: 15.08.06 06:47
Оценка:
MTP>Не совсем ясно, что означает появление переменной sign в бинарнике, т.к. она не имеет связывания. Если же речь идет о числовом литерале 0xC0DEC0DE, то понятие связывания к нему неприменимо и его представление определяется реализацией, на моей VC 7.1 константа подставляется непосредственно в код каждой из функций foo.
да, я имел ввиду числовой литерал, я ввел его чтобы сразу было видно в бинарнике сколько тел функций получилось (надо было ещё volatile добавить на всякий случай), но потом на всякий случай отдизасмил бинарники чтобы точно убедиться что там две или одна функции

MTP>Это странное поведение, в asm-листинге и объектных файлах я получил по 2 независимых экземпляра foo на доступных мне компиляторах, в том числе на VC 7.1, как и ожидалось. В имя foo после декорирования тем или иным образом включается уникальный идентификатор единицы трасляции. Возможно, линкер оказался достаточно умным, чтобы выкинуть дублирующиеся определения, пока в коде нет взятия адреса.

vc7.1, вот три варианта моих бинарников http://rsdn.ru/File/57441/bin.rar (static — 2 тела функции foo(), namespace, inline — 1 тело функции foo())

M>>мне нужно чтобы была только одна копия функции, inline размажет функции в каждый объектник

MTP>C unnamed namespace будет то же самое. Чем не устраивает стандартная практика размещать в заголовочных файлах только объявления функций?
просто сниппеты обычно маленькие и самодостаточные и не хочется разбивать их на utils.h(классы)/utils.cpp(функции) это не удобно, хочется всё держать в одном utils.h

AL>Корректный способ решения, на мой взгляд -- поместить функцию в именованное пространство имен (или в global scope) и объявить ее inline. Современные компиляторы все равно сами решают, встраивать функцию или нет (даже если она и не объявлена inline), а вот проблемы с ODR и статическими переменными внутри функций будут сняты.

and a special thanks goes to a-lex, решение с inline просто великолепно подошло для моих нужд
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.