Re: Ещё один вопрос про определение статического члена шаблонного класса
От: Erop Россия  
Дата: 11.10.13 07:47
Оценка:
Здравствуйте, Molchalnik, Вы писали:

M>Помогите достичь просветления.


По идее должно работать уже это:
template <typename Arg> class Ft {
public:
    static VectorT p_;
}; 

template<typename Arg> std::vector<Arg> Ft<Arg>::p_;

int main( int, char* [] ) { return Ft<int>::p_.size(); }
Но, похоже, в gcc какая-то ошибка на эту тему.

Когда ты добавляешь extern, gcc, похоже, воспринимает это как какое-то хитрое объявление, а не определение.

Попробуй написать так:
template<typename Arg> std::vector<Arg> Ft<Arg>::p_(0);
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Ещё один вопрос про определение статического члена шаблонного класса
От: Evgeny.Panasyuk Россия  
Дата: 11.10.13 08:39
Оценка:
Здравствуйте, Erop, Вы писали:

E>По идее должно работать уже это:

E>Но, похоже, в gcc какая-то ошибка на эту тему.
E>Когда ты добавляешь extern, gcc, похоже, воспринимает это как какое-то хитрое объявление, а не определение.
E>Попробуй написать так:
template<typename Arg> std::vector<Arg> Ft<Arg>::p_(0);
E>


Приведи полный код примера. На самом старом GCC который есть под рукой — 4.1.2, работает без (0)
Re[5]: Ещё один вопрос про определение статического члена шаблонного класса
От: B0FEE664  
Дата: 11.10.13 08:43
Оценка: 1 (1)
Здравствуйте, Molchalnik, Вы писали:

M>Кодт, то, что ты написал в примере, красиво и понятно, я это знаю и так и делаю. Так ведь не работает же! Должно работать в теории, но не работает. Точнее, работает, пока cpp файл один. Как только начинаешь обращаться к инстанции шаблонного класса из двух cpp (или даже из одного, но не того, в котором дано определение статического члена-данного шаблонного класса), начинается разнообразная хрень. Написали здесь много чего, но ответа на свой вопрос я так не получил. У меня gcc 4.7.2 для убунты, возможно, это фишка или баг gcc



Всё работает:
gcc -v
  Скрытый текст
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/i686-linux-gnu/4.6/lto-wrapper
Target: i686-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.6.3-1ubuntu5' --with-bugurl=file:///usr/share/doc/gcc-4.6/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.6 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.6 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --enable-objc-gc --enable-targets=all --disable-werror --with-arch-32=i686 --with-tune=generic --enable-checking=release --build=i686-linux-gnu --host=i686-linux-gnu --target=i686-linux-gnu
Thread model: posix

>gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)


//test.h
#include <vector>

template <typename Arg> class Ft {
 public:
  typedef std::vector<Arg> VectorT;
  static VectorT m_table;
};
template <class Arg> typename Ft<Arg>::VectorT Ft<Arg>::m_table;



//test1.cpp
#include "test.h"

void testFt1(int n)
{
  Ft<int>::m_table.push_back(n);
  Ft<int> oFt;
  oFt.m_table.push_back(2*n);
}


void testFt2(Ft<int>& rFt)
{
  rFt.m_table.push_back(3);
  rFt.m_table.push_back(4);
}



//test.cpp
#include <iostream>
#include "test.h"

extern void testFt1(int n);
extern void testFt2(Ft<int>& rFt);

int main(int argc, char * argv[])
{
  Ft<int> oFt;
  oFt.m_table.push_back(0);

  testFt1(1);
  testFt2(oFt);

  for(int i = 0; i < oFt.m_table.size(); i++)
    std::cout << "m_table[" << i << "]=" << oFt.m_table[i] << std::endl;

  std::cout << "The end" << std::endl;
  return 0;
}


g++ -c -I. -I../stereocam/trunk -DDEBUG -std=c++0x -MD -o build/debug/test.o test.cpp
g++ -c -I. -I../stereocam/trunk -DDEBUG -std=c++0x -MD -o build/debug/test1.o test1.cpp
g++ build/debug/test.o build/debug/test1.o -o bin/debug/test

Executable module at bin/debug/test


>bin/debug/test

m_table[0]=0
m_table[1]=1
m_table[2]=2
m_table[3]=3
m_table[4]=4
The end

test.h
И каждый день — без права на ошибку...
Re[3]: Ещё один вопрос про определение статического члена шаблонного класса
От: Erop Россия  
Дата: 11.10.13 09:28
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Приведи полный код примера. На самом старом GCC который есть под рукой — 4.1.2, работает без (0)


Понятно, что и без (0) должно, просто это вариант обойти багу.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: Ещё один вопрос про определение статического члена шаблонного класса
От: Evgeny.Panasyuk Россия  
Дата: 11.10.13 09:33
Оценка:
Здравствуйте, Erop, Вы писали:

EP>>Приведи полный код примера. На самом старом GCC который есть под рукой — 4.1.2, работает без (0)

E>Понятно, что и без (0) должно, просто это вариант обойти багу.

Я пытаюсь понять на каком примере и на каком компиляторе у тебя вылез такой баг.
Re[5]: Ещё один вопрос про определение статического члена шаблонного класса
От: Erop Россия  
Дата: 11.10.13 10:49
Оценка: :)
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Я пытаюсь понять на каком примере и на каком компиляторе у тебя вылез такой баг.

У меня не вылез...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Ещё один вопрос про определение статического члена шаблонного класса
От: Molchalnik  
Дата: 11.10.13 11:15
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>

BFE>Всё работает:

Ога, так всё работает. Просто не надо объявлять статическую переменную в cpp. или объявлять в обоих. Я так тоже пробовал, но почему-то "не пошло". Может, это был баг gcc, но я думаю, просто накосячил где-то. Теперь всё работает. Спасибо.
Re[6]: Ещё один вопрос про определение статического члена шаблонного класса
От: Evgeny.Panasyuk Россия  
Дата: 11.10.13 11:21
Оценка:
Здравствуйте, Erop, Вы писали:

EP>>Я пытаюсь понять на каком примере и на каком компиляторе у тебя вылез такой баг.

E>У меня не вылез...

Ну так между тем что ты показал и тем что сделал ТС — разница в одно определение (точнее оно было, но не в заголовке, а в одном из TU. причём, как я понял, в том TU не было нужных instatiations, а в том TU где использовалось — не было видно определения)
То есть там баг в коде, а не в GCC.
Re[7]: Ещё один вопрос про определение статического члена шаблонного класса
От: Molchalnik  
Дата: 11.10.13 11:54
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Ну так между тем что ты показал и тем что сделал ТС — разница в одно определение (точнее оно было, но не в заголовке, а в одном из TU. причём, как я понял, в том TU не было нужных instatiations, а в том TU где использовалось — не было видно определения)


Точно так. А когда я сделал так, как показал B0FEE664, я накосячил, и у меня вылез баг, и я запихнул определение в cpp. У меня всё заработало и я забил. А потом появилось обращение из другого cpp, и началась ерунда.

Но просветление не наступило. Я не получил ответа на свой вопрос, если не считать Кодта с его ODR. Просветление — это понимание не на уровне "ты туда ходи, а не сюда" "определяй статический шаблон в хедере, а не в сипипи", а понимание на уровне принципов.

Я понимаю, что хедер тупо вставляется в cpp, и никакого хедера для компилера на самом деле нет ( "ложки нет"(С) ). В результате после инклюда компилер имеет два сипипи с длинной простынёй одинаковых определений шаблонов, стандартных и созданных программистом. Если статическая переменная шаблона объявлена в неком cpp, то она не объявлена в другом cpp.

Так вот, почему просто статическую переменную можно определить в одном cpp, а статическую переменную шаблона нужно определять в каждом cpp, или, что одно и тоже, в хедере?
Re[8]: Ещё один вопрос про определение статического члена шаблонного класса
От: Evgeny.Panasyuk Россия  
Дата: 11.10.13 12:06
Оценка:
Здравствуйте, Molchalnik, Вы писали:

M>Так вот, почему просто статическую переменную можно определить в одном cpp, а статическую переменную шаблона нужно определять в каждом cpp, или, что одно и тоже, в хедере?


Потому что
struct Gadget {};
 
template<typename T>
struct Widget
{
    static Gadget x;
};
у Widget<int> будет своя статическая переменная, у Widget<double> — своя.
Когда
template<typename T> Gadget Widget<T>::x;
есть только в одном translation unit — он инстанциирует static member только для тех вариантов Widget<???> которые сам использует.
Он ничего не знает про то, какие Widget<???> используются в других TU. А инстанциировать для всех возможных типов Widget<???> он не может, так как их бесконечно много.
Re[9]: Ещё один вопрос про определение статического члена шаблонного класса
От: Evgeny.Panasyuk Россия  
Дата: 11.10.13 12:22
Оценка:
EP>Он ничего не знает про то, какие Widget<???> используются в других TU. А инстанциировать для всех возможных типов Widget<???> он не может, так как их бесконечно много.

Пример для большего понимания:
$ find ./ -type f | xargs -i% sh -c "echo ________[%]________ && cat %"
________[./main.cpp]________
#include "t.h"

int main()
{
     int x = Widget<int>::x.value;
     int y = Widget<double>::x.value;
}
________[./t.cpp]________
#include "t.h"

template<typename T> Gadget Widget<T>::x;

void foo()
{
    int x = Widget<int>::x.value;
}
________[./t.h]________
struct Gadget { int value; };

template<typename T>
struct Widget
{
    static Gadget x;
};

$ g++ main.cpp t.cpp
main.cpp:(.text+0x18): undefined reference to `Widget<double>::x'

Как видно, ругается только на отсутствие Widget<double>::x, но не Widget<int>::x — так как Widget<int>::x инстанциировался в t.cpp.
Но t.cpp ничего не знает про то, что main.cpp нужен Widget<double>::x.
Re[9]: Ещё один вопрос про определение статического члена шаблонного класса
От: Molchalnik  
Дата: 11.10.13 12:26
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>[/ccode] есть только в одном translation unit — он инстанциирует static member только для тех вариантов Widget<???> которые сам использует.

EP>Он ничего не знает про то, какие Widget<???> используются в других TU. А инстанциировать для всех возможных типов Widget<???> он не может, так как их бесконечно много.

А как компилер поступает (и как должен поступать), если в двух TU используется одна и таже инстанция шаблона, например, Widget<int> ?
Как-то же он линкует статические данные Widget<int>::x, чтобы в двух модулях они были одной и той же переменной, а не двумя разными
Re[10]: Ещё один вопрос про определение статического члена шаблонного класса
От: Evgeny.Panasyuk Россия  
Дата: 11.10.13 12:53
Оценка:
Здравствуйте, Molchalnik, Вы писали:

M>А как компилер поступает (и как должен поступать), если в двух TU используется одна и таже инстанция шаблона, например, Widget<int> ?

M>Как-то же он линкует статические данные Widget<int>::x, чтобы в двух модулях они были одной и той же переменной, а не двумя разными

Примерно также как и с inline функциями — о чём и сказал Кодт в первом
Автор: Кодт
Дата: 10.10.13
же сообщении.
live demo:
struct Gadget { int value; };

template<typename T>
struct Widget
{
    static Gadget x;
};
template<typename T> Gadget Widget<T>::x;

struct Button
{
    static Gadget x;
};
Gadget Button::x;

int main()
{
    int x = Widget<int>::x.value;
}
/****************/
g++ -c main.cpp && nm main.o
0000000000000000 B _ZN6Button1xE
0000000000000000 u _ZN6WidgetIiE1xE
0000000000000000 T main

GNU nm:

If lowercase, the symbol is usually local; if uppercase, the symbol is global (external). There are however a few lowercase symbols that are shown for special global symbols (u, v and w).
...
B
b
The symbol is in the uninitialized data section (known as BSS).
...
u
The symbol is a unique global symbol. This is a GNU extension to the standard set of ELF symbol bindings. For such a symbol the dynamic linker will make sure that in the entire process there is just one symbol with this name and type in use.

Re[11]: Ещё один вопрос про определение статического члена шаблонного класса
От: Evgeny.Panasyuk Россия  
Дата: 11.10.13 12:59
Оценка:
А если закомментировать определение:
//template<typename T> Gadget Widget<T>::x;

то будет:
0000000000000000 B _ZN6Button1xE
                 U _ZN6WidgetIiE1xE
0000000000000000 T main

U
The symbol is undefined.

Re[8]: Ещё один вопрос про определение статического члена шаблонного класса
От: Кодт Россия  
Дата: 11.10.13 15:58
Оценка: 2 (1)
Здравствуйте, Molchalnik, Вы писали:

M>Я понимаю, что хедер тупо вставляется в cpp, и никакого хедера для компилера на самом деле нет ( "ложки нет"(С) ). В результате после инклюда компилер имеет два сипипи с длинной простынёй одинаковых определений шаблонов, стандартных и созданных программистом. Если статическая переменная шаблона объявлена в неком cpp, то она не объявлена в другом cpp.


А ты говоришь, просветление не наступило.

M>Так вот, почему просто статическую переменную можно определить в одном cpp, а статическую переменную шаблона нужно определять в каждом cpp, или, что одно и тоже, в хедере?


Потому что определение члена шаблона — это, на самом деле, шаблон определения члена. (Неважно, статическая переменная или функция).
Определением конкретного символа линковки (как это по-рюсски, в терминах стандарта?) он становится только при воплощении (instantiation) — явном или по факту использования.

Отвлечёмся от статических членов — пусть будут свободные функции
//////////
// x.cpp

template<class T> void foo(); // объявление

void x() { foo<int>(); foo<char>(); } // для линкера - ссылки на foo<int>, foo<char>

//////////
// y.cpp

template<class T> void foo(); // объявление
template<class T> void foo() {} // определение

void y() { foo<int>(); foo<short>(); } // для линкера - ссылки на foo<int>, foo<char>, плюс созданы соответствующие воплощения

template void foo<long>(); // создано, но не используется воплощение foo<long>

При линковке — линкер будет искать foo<int>, foo<char>, foo<short> и найдёт foo<int>, foo<short>, foo<long>.
Перекуём баги на фичи!
Re[9]: Ещё один вопрос про определение статического члена шаблонного класса
От: Molchalnik  
Дата: 11.10.13 21:30
Оценка:
Здравствуйте, Кодт, Вы писали:

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


M>>Я понимаю, что хедер тупо вставляется в cpp, и никакого хедера для компилера на самом деле нет ( "ложки нет"(С) ). В результате после инклюда компилер имеет два сипипи с длинной простынёй одинаковых определений шаблонов, стандартных и созданных программистом. Если статическая переменная шаблона объявлена в неком cpp, то она не объявлена в другом cpp.


К>А ты говоришь, просветление не наступило.


Так это я уже десять лет как знаю.

К>Потому что определение члена шаблона — это, на самом деле, шаблон определения члена. (Неважно, статическая переменная или функция).

К>Определением конкретного символа линковки (как это по-рюсски, в терминах стандарта?) он становится только при воплощении (instantiation) — явном или по факту использования.

Спасибо, одна моя ладонь хлопнула. Просветление меня настигло. Мне как раз и надо было описание ситуации в терминах стандарта и с точки зрения компилера/линкера
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.