Здравствуйте, Erop, Вы писали:
E>Не, ну это ты себе льстишь всё-таки. Хотя хак классный, жалко, что не по стандарту E>Для того, чтобы производители компилятора поддерживали в следующих версиях багу, как фичу, требуется, чтобы багу начали использовать таки массово
Ну есть немного
А для того, что бы багу исправили в следующей версии компилятора тоже требуется, что бы багу "начали использовать массово". Т.ч. я всё равно не думаю, что М$ будет париться и исправлять *такой* баг, мне даже его субмитить лень.
Вот я один им отправлял здесь, так они ответили:
[бла-бла-бла, мы не будем исправлять баг, т.к] as the code is convoluted and does not reflect the majority of real world use cases
Здравствуйте, remark, Вы писали:
R>Здравствуйте, tilarids, Вы писали:
T>>Comeau Online демонстрирует вообще замечательное поведение — там просто ADL не работает.
R>LROTF
R>Ты тут просто неправильно написал. Ты не ту функцию другом объявляешь или точнее другом не того класса. R>Хинт: тип flag<no> и тип flag<yes> — совершенно разные типы. При ADL поиске через один тип не будут находится функции связанные с другим типом. R>Если не изменяет память, в этом топике я приводил решение для этой проблемы.
R>
Так. Сейчас будем очень аккуратно разбираться. Дело в том, что это твой же пример — я просто его чуть-чуть переделал. И поверил тебе, что там ADL. Могу даже на твоем же коде продемонстрировать.
char engine(...);//Всего одна функцияtemplate<typename, int> struct magic;
typedef magic<char, -1> magicc;//При typedef ничего не инстанцируется - все еще одна функцияtemplate<typename type = int, int id = sizeof(engine(*(magicc*)0, *(type*)0))>
//magicc снова не инстанцируется - это не нужно
//то есть, в magicc:: нет friend int engine(magicc&, type&);
//получается id = sizeof возвращаемого значения первой функции(с эллипсисом)struct magic
{
friend int engine(magicc&, type&);//появляется magic<int,sizeof(char)>::engine(magicc&, type&)
//При следуюущем вызове ADL действительно работать не будет, ведь ADL по magicc - ничего не найдет, ибо magicc не инстанцируется никогда.static const int val = id;
};
int main()
{
char a[magic<>::val != magic<>::val ? 1 : -1];//Почему же тогда это работает в стольких компиляторах????
}
По поводу моего примера — я бы не присылал его, если бы не проверил — он работает также, как и твой. Во всех компайлерах, мне доступных.
Comeau действительно отрабатывает правильно. Потому что нет ADL. Я решил туда его добавить — для этого нужно просто инстанцировать класс, который передается параметром в функцию ПОСЛЕ первого вызова этой функции. Сделал вот такую вещь:
struct yes{};
struct no{char _no[2];};
no check(...);
template <class,class,int> struct flag;
template <class T> T& make_ref();
template <class T> T* make_ptr();
template <typename T> yes is_same_tester(T*, T*);
no is_same_tester(...);
template< class T1, class T2 >
struct is_same//Проверка типов на равенство
{
enum { value = sizeof(is_same_tester(make_ptr<T1>(),make_ptr<T2>()))==sizeof(yes)};
};
template <int val,class true_class,class false_class>
struct typeselector;//Выбирает тип в зависимости от значения valtemplate <class true_class,class false_class>
struct typeselector<0,true_class,false_class>
{
typedef false_class type;//если 0 - то здесь - false_class
};
template <class true_class,class false_class>
struct typeselector<1,true_class,false_class>
{
typedef true_class type; //если 1 - то здесь - true_class
};
class Dummy//пустой класс, нужен для наследования
{
};
template <class flag_type,class type = yes, int id = sizeof(check(make_ref<flag<flag_type,no,-1> >(), make_ref<type>()))==sizeof(yes)>
struct flag : public typeselector<is_same<flag<flag_type,type,id>,flag<flag_type,no,-1> >::value,Dummy,flag<flag_type,no,-1> >::type
//Вся идея заключается в применении наследования для форсирования инстанцирования нужного нам класса. Так как оно должно обрабатываться после подсчёта default параметров, то мы все так же получаем 0 при первом инстанцировании.
//Так как наследоваться от самого себя нельзя, то делаем проверку типов, и в случае инстанцирования flag<flag_type,no,-1> наследуемся от Dummy
{
friend yes check(flag<flag_type,no,-1>&,type&);
static const int IsTrue = id;
};
char a[flag<int>::IsTrue == 0 ? 1 : -1];//0char b[flag<int>::IsTrue == 1 ? 1 : -1];//1 - вот здесь комо валится
//Все прекрасно работает на студии, но все так же не работает на Comeau
Извиняюсь за корявость примера, возможно там где-то ошибки внутри кода. Если кто сможет написать, чтобы работало ADL без всяких лишних телодвижений(элементарная подстановка в расчёте id другого имени класса почему-то приводит к тому, что все перестает работать) — просим. Кто сможет объяснить — в чем я не прав, или, в другом случае, привести более короткий и красивый пример — буду рад.
PS. Кстати, комо инстанцирует шаблоны в моем случае именно в таком порядке:
1. flag<int,yes,0>
2. flag<int,no,-1>//Первые два определил по сообщениям об ошибках
3. Не инстанцируется flag<int,yes,1>
<вырезано, дабы сохранить место на сервере>
Re[6]: Невероятно, но факт! Не константные значения в компай
Здравствуйте, tilarids, Вы писали:
T> char a[magic<>::val != magic<>::val ? 1 : -1];//Почему же тогда это работает в стольких компиляторах????
Потому что на них на всех ещё одна бага, они считают, что flag<yes> и flag<no> один тип в плане ADL и друзей.
T>char b[flag<int>::IsTrue == 1 ? 1 : -1];//1 — вот здесь комо валится
Всё правильно валится, он то как раз по стандарту всё делает.
Здравствуйте, Erop, Вы писали:
E>ИМХО этот код работать не должен. Это если по стандарту.
E>Чтобы убедить меня в обратном, укажи пожалуйста в каких именно местах единицы трансляции должны находиться POI каких именно шаблонов, E>чтобы в этой строчке
"compile_time_flag::flag<int>::IsTrue" компилировалось по разному слева и справа от оператора !=.
E>То что на остальных компиляторах этот хак работает не о чём, кроме неаккуратного использования в этих компиляторах статических переменных не говорит
Я тоже согласен, что код работать никак не должен. Ибо результат не должен зависеть от порядка инстанцирования шаблонов. Я хочу сделать нечто другое: некое подобие init. То бишь, меня не волнует, в каком порядке инстанцировались шаблоны. Мне хочется, чтобы если где-нибудь, или когда-нибудь участвовал некий специально сформированный шаблон, то автоматически где-нибудь некая сущность это в себе хранила. Что-то вроде:
//идеальный вариант
work_if<verifier<type>::inited,Dummy,Worker>().Work();//Использует класс в зависимости от значения
init<type>::init;//Если оно здесь есть, то verifier<type>::inited везде становится равным 1, иначе - 0
work_if<verifier<type>::inited,Dummy,Worker>().Work();//Использует класс в зависимости от значения
work_if<verifier<type>::inited,Dummy,Worker>().Work();//Использует класс в зависимости от значения
//удовлетворюсь таким - раз уж столько компиляторов подряд инстанцируют
//его уже можно реализовать - могу кинуть код - жаль, не свой.
init<type>::init;//Если оно здесь есть, то verifier<type>::inited везде становится равным 1, иначе - 0
work_if<verifier<type>::inited,Dummy,Worker>().Work();//Использует класс в зависимости от значения
work_if<verifier<type>::inited,Dummy,Worker>().Work();//Использует класс в зависимости от значения
work_if<verifier<type>::inited,Dummy,Worker>().Work();//Использует класс в зависимости от значения
PS. Пока писал — появились идеи
<вырезано, дабы сохранить место на сервере>
Re[7]: Невероятно, но факт! Не константные значения в компай
Здравствуйте, remark, Вы писали:
R>Здравствуйте, tilarids, Вы писали:
T>> char a[magic<>::val != magic<>::val ? 1 : -1];//Почему же тогда это работает в стольких компиляторах????
R>Потому что на них на всех ещё одна бага, они считают, что flag<yes> и flag<no> один тип в плане ADL и друзей.
T>>char b[flag<int>::IsTrue == 1 ? 1 : -1];//1 — вот здесь комо валится
R>Всё правильно валится, он то как раз по стандарту всё делает.
R>
Дело в том, что раз Comeau работает по стандарту, значит, он должен найти
T>>Он там точно есть. Если ADL не должен находить его — то что же вообще он должен находить? Это ведь именно тот же самый тип!
R>Да, должен найти, только в обеих случаях, а не только во втором.
Ну, это стандартом не оговорено, кажется, — что должно происходить вначале — подсчёт дефолтовых параметров, или включение в loookup всех внутренних функций. Если оговорено — цитату, пожалуйста. А то Comeau пока в меньшинстве — мне более логичным на моем примере кажется поведение именно всех остальных компиляторов(точнее, поведение на нем студии )
T>...Мне хочется, чтобы если где-нибудь, или когда-нибудь участвовал некий специально сформированный шаблон, то автоматически где-нибудь некая сущность это в себе хранила. Что-то вроде:
Ну для этого ADL вроде как подходит...
Хотя всё равно только в пределах единицы трансляции. А что будет с базой шаблонов --
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, tilarids, Вы писали:
T>>...Мне хочется, чтобы если где-нибудь, или когда-нибудь участвовал некий специально сформированный шаблон, то автоматически где-нибудь некая сущность это в себе хранила. Что-то вроде:
E>Ну для этого ADL вроде как подходит...
E>Хотя всё равно только в пределах единицы трансляции. А что будет с базой шаблонов --
Переписал все заново — теперь все выглядит намного проще. Чистый код:
int main()
{
using namespace compile_time_flag;
static const int j = CHECK(int);
INIT (int);
static const int i = CHECK(int);
char b[i!=j?1:-1];
(void)b;
return 0;
}
Some more files:
"RealWorker.h"
#pragma once
#include <iostream>
class CRealWorker
{
public:
CRealWorker(void);
~CRealWorker(void);
void Work()
{
std::cout<<"Real work goes here!!!\n";
}
};
"DummyWorker.h"
#pragma once
#include <iostream>
class CDummyWorker
{
public:
CDummyWorker(void);
~CDummyWorker(void);
void Work()
{
std::cout<<"Just relaxing :)\n";
}
};
"Worker1.h"
#pragma once
#include"DummyWorker.h"#include"RealWorker.h"#include"compile_time_flag.h"template <int value>
class CWorker1;
template <>
class CWorker1<0> : public CDummyWorker
{
};
template <>
class CWorker1<1> : public CRealWorker
{
};
struct Worker{};
#define CWorker CWorker1< CHECK(Worker) >
#define InitWorker() INIT(Worker)
Usage:
int main()
{
using namespace compile_time_flag;
CWorker w1;
InitWorker();
CWorker w2;
w1.Work();//dummy
w2.Work();//workreturn 0;
}
Как видно, использовать можно как и в простых случаях, так и в более сложных структурах. Естественно, с большой уверенностью, что именно ты делаешь
И самое приятное. Компилируется и работает в VC8, а также в Comeau Online и здесь в MINGW/C++ и EDG/C++.
PS. Проверял только Simple usage в онлайн компайлерах, по причинам вполне понятным
T>int main()
T>{
T> using namespace compile_time_flag;
T> static const int j = CHECK(int);
T> INIT (int);
T> static const int i = CHECK(int);
T> char b[i!=j?1:-1];
T> (void)b;
T> return 0;
T>}
T>
Двухфазный механизм возможно дает тоже. Не сообрауже сразу чем бинарный CHECK INIT может быть лучше.
Можно еще очистить этот код, чтоб вообще никакой магии не было
T>int main()
T>{
T> using namespace compile_time_flag;
T> static const int j = CHECK(int);
T> INIT (int);
T> static const int i = CHECK(int);
T> char b[i!=j?1:-1];
T> (void)b;
T> return 0;
T>}
T>
Ну типа A<int> -- локальный класс?
По идее, если по стандарту, то POI A<int> должно бы быть до функции main, так как A<int> вроде как с внешним связыванием...
T>PS. Проверял только Simple usage в онлайн компайлерах, по причинам вполне понятным
ИМХО в любой момент могёт рвануть....
Ну и главное, можно проще намного сделать.
Типа так:
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[10]: Невероятно, но факт! Не константные значения в компа
Здравствуйте, tilarids, Вы писали:
T>Ну, это стандартом не оговорено, кажется, — что должно происходить вначале — подсчёт дефолтовых параметров, или включение в loookup всех внутренних функций. Если оговорено — цитату, пожалуйста. А то Comeau пока в меньшинстве — мне более логичным на моем примере кажется поведение именно всех остальных компиляторов(точнее, поведение на нем студии )
Согласен — нормальное поведение компиляторов. Все компилирую. Правда VC6 захотел имена в предварительной декларации и енум вместо статик. А Comeau известен своей глючностью
int main()
{
compile_time_flag::flag<float> f0a,f0b;
compile_time_flag::flag<float> f1a,f1b;
int idv[]={f0b.IsTrue,f1b.IsTrue}; // разные значения
f0a=f0b;
f1a=f1b;
f0a=f1a; // присваивание невозможно - ошибка
}
В С полно таких мест когда один и тоже код ведет себя по разному в различных местах. А заставлять компилятор лазить внутрь — это превращать его в пролог Поведение компилятора должно быть как можно проще, тогда все предсказуемо будет
Re[10]: Невероятно, но факт! Не константные значения в компа
Здравствуйте, tilarids, Вы писали:
R>>Да, должен найти, только в обеих случаях, а не только во втором.
T>Ну, это стандартом не оговорено, кажется, — что должно происходить вначале — подсчёт дефолтовых параметров, или включение в loookup всех внутренних функций. Если оговорено — цитату, пожалуйста. А то Comeau пока в меньшинстве — мне более логичным на моем примере кажется поведение именно всех остальных компиляторов(точнее, поведение на нем студии )
Тут дело не в том, что раньше: подсчёт дефолтовых параметров, или включение в loookup всех внутренних функций.
Тут всё проше — если функция может быть найдена, то она должна быть найдена.
Ссылку из стандарта ISO привести сложно, т.к. это базовая вещь. Нет какого-то одного абзаца, который конкретно разъяснял всю эту ситуацию. Тут смешано очень много вещей.
Общая идея такая. (Это как раз, то что я пытался опровергнуть, но не получилось. Ну точнее не получилось в соотв. со стандартом ISO, а на большинстве компиляторов как-раз получилось).
У компилятора в процессе компиляции нет контекста (т.е. состояния). Точнее нет изменяемого контекста (т.е. того, на которой может влиять программист). Т.е. у компилятора в каждой точке программы есть один чётко определённый контекст. Соответственно отсюда следует, что в принципе не может быть ничего о чём я писал — главное — не может быть "переменных", т.е. одно и тоже выражение в одном и том же контексте (читай — месте программы) не может иметь разных значений. И в этом принципиальное отличие от "ран-тайм".
Здравствуйте, remark, Вы писали:
R>Здравствуйте, tilarids, Вы писали:
T>>И самое приятное. Компилируется и работает в VC8, а также в Comeau Online
R>Ты попробуй Comeau Online не с dinkumware, а который они сами предлагают. На dinkumware старая версия, а они похоже это исправляли как раз недавно.
R>
Проверял здесь . Кажется, официальный вариант Если же имеется ввиду оффлайн компайлер+линкер — то на него я денег не имею
В общем, нити, повторное обсуждение в которых я спровоцировал , можно считать закрытыми. Действительно, такой трюк скорее восстает не против стандарта, а против здравого смысла. Кроме того, для себя я понял, что Comeau не так хорош в поддержке стандарта, как его рисуют(как, в принципе и остальные компиляторы — но это не мешает им работать).
Теперь осталось найти язык, сходный с С++ по системным возможностям(а лучше — позволяющий использовать С++ конструкции в каком-либо удобном виде).
Здравствуйте, tilarids, Вы писали:
T>Проверял здесь . Кажется, официальный вариант Если же имеется ввиду оффлайн компайлер+линкер — то на него я денег не имею
Ну так и чего на нём? Работает или нет? В смысле, наверное правильнее спрашивать, как работает?
T>В общем, нити, повторное обсуждение в которых я спровоцировал , можно считать закрытыми. Действительно, такой трюк скорее восстает не против стандарта, а против здравого смысла. Кроме того, для себя я понял, что Comeau не так хорош в поддержке стандарта, как его рисуют(как, в принципе и остальные компиляторы — но это не мешает им работать).
А чем Comeau не соотв. стандарту? Как раз вроде он один и соотв.
T>Теперь осталось найти язык, сходный с С++ по системным возможностям(а лучше — позволяющий использовать С++ конструкции в каком-либо удобном виде).
Здравствуйте, tilarids, Вы писали:
T>...такой трюк скорее восстает не против стандарта, а против здравого смысла.
Ну связь здравого смысла и стандарта С++ темна для меня. Но мне таки кажется, что такой трюк не должен работать именно по стандарту. Или укажи таки POI задействованных шаблонов
И главное, чем тебе просто объявлять функцию какого-то прототипа, в макросе INIT не нравится? Всё по стандарту, без хаков и надёжно вроде...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, remark, Вы писали:
R>Здравствуйте, tilarids, Вы писали:
T>>Проверял здесь . Кажется, официальный вариант Если же имеется ввиду оффлайн компайлер+линкер — то на него я денег не имею
R>Ну так и чего на нём? Работает или нет? В смысле, наверное правильнее спрашивать, как работает?
Интересно как именно он работает
namespace compile_time_flag
{
struct yes{};
struct no{char _no[2];};
no check(...);
template <class,class,int> struct flag;
template <class T> T& make_ref();
template <class flag_type,class type = yes
, int id = sizeof(check(make_ref<flag<flag_type,no,-1> >(), make_ref<type>()))==sizeof(yes)>
struct flag
{ friend yes check(flag<flag_type,no,-1>&,type&);
enum { IsTrue = id};
};
}
int main()
{
compile_time_flag::flag<float> f0a,f0b;
compile_time_flag::flag<float> f1a,f1b;
int idv[]={f0b.IsTrue,f1b.IsTrue}; // разные значения
f0a=f0b;
f1a=f1b;
f0a=f1a; //присваивание разных инстанций - ошибка в VC8 ok Comeau
}
У VC8 первая переменная типа <.., no, 0> вторая типа <.., no, 1> у Comeau одног. Интересно какого?
namespace compile_time_flag
{
struct yes{} dyes;
struct no{char _no[2];};
no check(...);
template <class,class,int> struct flag;
template <class T> T& make_ref();
template <class flag_type,class type = yes, int id = sizeof(check(make_ref<flag<flag_type,no,-1> >(), make_ref<type>()))==sizeof(yes)>
struct flag
{ friend yes check(flag<flag_type,no,-1>&,type&);
// отсюда начинается видимость yes checkenum { IsTrue = id};
};
// yes check(flag<float,no,-1>&,yes&);
// это таки ломает его
}
using namespace compile_time_flag;
int main()
{ compile_time_flag::flag<float,compile_time_flag::yes,0> d0;
compile_time_flag::flag<float,compile_time_flag::no,-1> f0;
yes yy=check(f0,dyes);
// здесь yes check точно видна во время исполнения
compile_time_flag::flag<float> f0a,f0b;
compile_time_flag::flag<float> f1a,f1b;
int idv[]={f0b.IsTrue,f1b.IsTrue};
f0a=f0b;
f1a=f1b;
d0=f0a; // проверяем что f0a типа compile_time_flag::flag<float,compile_time_flag::yes,0>
// поскольку 0 yes check() не учлась в шаблон, тоесть не видна
f0a=f1a; // второй compile_time_flag::flag<float> тотже, тоесть с 0
}
namespace compile_time_flag
{ // сама функция
yes check(flag<float,no,-1>&,yes&)
{ return dyes;
}
}
Получаетс следующая картина деклараци френд делает функцию доступной для непосредственного вызова, но недоступной для шаблонов. Явное противоречие.
Комеау опять не со всеми, правда обычно он отказывается когда должен,
сейчас скомпилил когда не должен
void f(int);
f(1.);
void f(double);
f(1.);
Такой код никого не удивляет. Здесь аналогично. compile_time_flag::flag<float> должны быть 2 разных класса