scf37 проверь на gcc Код (Text): struct Iter { int& operator[](size_t _N) const { return x; } mutable int x; }; namespace Private { template <class _Tp> struct Dummy: _Tp { operator Dummy*() { return 0; } }; template <class _U> char t2(_U*, ...) { return 0; } template <class _Tp> char* t2(Dummy<_Tp>*, Dummy<_Tp>&) { return 0; } } template <class _Tp> struct Check { typedef Private::Dummy<_Tp> Type; enum { value = (sizeof(Private::t2((Type*)0, Type()[size_t(0)]))==sizeof(char)) }; }; int main() { int z = Check<Iter>::value; return 0; }
ух... работает... очень красивое решение - сделать для класса оператор преобразования к указателю на этот класс зачем нужен темплейтный первый параметр в t2() ? вроде и так работает Такой код сойдёт за конечный вариант? Код (Text): #include <list> #include <vector> #include <iostream> namespace Private { template <class _Tp> struct Dummy: _Tp { operator Dummy*() { return 0; } }; struct char2 //sizeof(char2) definitely larger than sizeof(v { char x[2]; }; char t2(int , ...) { return char(); } template <class _Tp> char2 t2(int, Dummy<_Tp>&) { return char2(); } } template <class _Tp> struct Check { typedef Private::Dummy<_Tp> Type; enum { value = (sizeof(Private::t2(0, Type()[int(0)]))==sizeof(char)) }; }; template <class _Tp> struct Check<_Tp *> { enum { value = 1 }; }; int main() { std::cout << "list: " << Check<std::list<int>::iterator>::value << std::endl; std::cout << "vector: " << Check<std::vector<int>::iterator>::value << std::endl; std::cout << "raw: " << Check<int *>::value << std::endl; return 0; } Увы, испытания на VC6 показали следующее: -std::vector::iterator - голый указатель -про частичные cпециализации шаблонов(14.5.4) он ничего не знает
да. ты прав. по стандарту - implementation defined для VC6.0 сырые указатели можно отсечь до основной проверки, использую факт неявного преобразования их в const volatile void*. но. имеем что-то вроде связанного списка Код (Text): struct Foo { int x; Foo* next; // следующий узел здесь }; Foo* p = .... проверка на неявное преобразование для p пройдет, но p+1 - неверно т.е. либо на них нужно забить. либо указать в доке, что если это сырой указатель, но блок памяти является непрерывным
это так. поэтому я говорил, что в 8.0 все проще. мой вариант, я проверял на 6.0, Intel 9.0 и самое главное Comeau online test тоже его съел (этого я не ожидал). насколько понял - gcc тоже. в моем варианте нет частичных специализаций и прочего "стандартного" поведения, к которому у 6.0 аллергия. я бы на твоем месте из-за VC6.0 не рубился - он фактически мертв и оставил реализацию на частичной специализации
Итак, бурные, продолжительные апплодисменты, переходящие в овации Вот вам подход, который позволяет детектить мембер-функции, возврящяющие не void. Имя класса, в котором происходит детект - Work. EDIT: Теперь можно и void Код (Text): struct Work { int foo() { return 0; } }; char foo() { return 0; } template <class R> char Proc(R (*)()) { return 0; } template <class T, class R> int Proc(R (T::*)()) { return 0; } struct A : public Work { enum { Value = sizeof(Proc(&foo)) }; }; int main() { int x = A::Value; return 0; }
И исчо адин вариант. Инкапсулируем вспомогательные функции + не засоряем глобал неймспейс. Код (Text): struct Work { void foo() { } }; namespace method_detection { char foo() { return 0; } struct A : public Work { private: template <class R> static char Proc(R (*)()) { return 0; } template <class T, class R> static int Proc(R (T::*)()) { return 0; } public: enum { Value = sizeof(Proc(&foo)) }; }; } int main() { int x = method_detection::A::Value; return 0; }
Поставим новую задачу Написать макрос, который будет учить компиллер детектить заданый метод в заданом классе. ЧТобы было типа так: Код (Text): DECLARE_DETECTION(Window, void, Draw, (int, int)) DECLARE_DETECTION(Window, int, GetWidth, ()) DECLARE_DETECTION(Window, int, GetHeight, ()) int main() { bool b1 = method_detection::Window::Draw::Present; bool b2 = method_detection::Window::GetWidth::Present; bool b3 = method_detection::Window::GetHeight::Present; return 0; } Это я конечно схематично. Ну вот чтобы было типа такого А там глядишь, может и в буст включат )) EDIT: Целесообразнее сделать следующее. Только пока не ясно как обойти невозможность дополнения класса. Код (Text): DECLARE_DETECTION(void, Draw, (int, int)) DECLARE_DETECTION(int, GetWidth, ()) DECLARE_DETECTION(int, GetHeight, ()) int main() { bool b1 = method_detection::Detector<Window>::DrawPresent; bool b2 = method_detection::Detector<Window>::GetWidthPresent; bool b3 = method_detection::Detector<Window>::GetHeightPresent; return 0; }
gcc 3.4.5 : main.cpp:36: error: ISO C++ forbids taking the address of a bound member function to form a pointer to member function. Say `&method_detection::A::foo' Под VC8 всё нормально работает. Я попытался немного переделать код: Код (Text): #include <iostream> namespace member_detection { struct char2 { char x[2]; }; //detecting // int method(char *, int) int method(char *, int) { return 0; } char2 Proc(int (*)(char *, int)) { return char2(); } template <class T> char Proc(int (T::*)(char *, int)) { return char(); } template<class T> class Check: public T { public: enum {Value= (sizeof(Proc(&method)) == sizeof(char)) }; }; } class Test { public: int method(char *, int) { return 0; } }; int main() { std::cout << member_detection::Check<Test>::Value; return 0; } Под VC8 всё нормально, gcc компилит, но программа стабильно выдаёт 0 вне зависимости от наличия int method(char *, int)
Nouzui Идея проста. В отличие от множественного наследования, выбор функций между мембер и фри имеет приоритет в сторону мембер. На этом месте компиллер и обманывается. scf37 А что скажет Камю?
_DEN_ Comeau: "ComeauTest.c", line 37: error: nonstandard form for taking the address of a member function Value = sizeof(Proc(&foo)) а если структура такая intel код не собирает.
scf37 "Кто хотел определения наличия функции-члена?" http://www.rsdn.ru/Forum/Message.aspx?mid=382857&only=1 приведенный код сам не тестировал
_DEN_ Вот так можно: Код (Text): #define DECLARE_METHOD_DETECTOR(detectorNameSpace, className, methodReturnValue, methodName, methodArgList) \ namespace detectorNameSpace{ \ template<methodReturnValue(className::*)methodArgList>struct Detector{typedef className Type;}; \ template<class T, class U> struct Exists {enum {val = false};}; \ template<class T> struct Exists<T, class Detector<&T::methodName>::Type>{enum {val = true};}; \ enum {exists = Exists<className, className>::val}; \ } // Пример использования class C { public: void operator !(); int Method(char, const void *); }; DECLARE_METHOD_DETECTOR(C_operator_not, C, void, operator!, ()) DECLARE_METHOD_DETECTOR(C_Method, C, int, Method, (char, const void *)) int _tmain(int argc, _TCHAR* argv[]) { printf("%d\n", C_operator_not::exists); printf("%d\n", C_Method::exists); return 0; } ---- Компилить в VC++ 6 и не пытайтесь.
green в VC8 работает, но на более ранних (в частности 7.1) - нет а вообще мощно.. когда я увидел тему, я подумал, что без __if_exists обойтись просто невозможно
да, кстати.. изначально речь шла об операторе индексирования, а ошиба C2801 утверждает: 'operator []' must be a non-static member это я к тому, что фришный оператор индексирования объявить не так-то легко..