Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > (g++)указатель на шаблонную функцию


Автор: GoldFinch 27.11.2010, 15:20
Код

typedef void(*callback_t)(void*);
void function_with_callback(callback_t callback, void* param)
{
    callback(param);
}

template<class T>
void method_to_fn(T* that)
{
    that->bar();
}

#include <iostream>

struct Foo
{
    void bar() { std::cout << "Foo::bar OK\n"; }
};

int main()
{
    //function_with_callback((callback_t)method_to_fn<Foo>, NULL);
    function_with_callback((callback_t)static_cast<void(*)(Foo*)>(method_to_fn<Foo>), NULL);
}

этот код работает.
а если раскомментировать 22ю строчку, то он будет работать в MSVC, а g++ выдаст ошибку 
Код

 error: insufficient contextual information to determine type

http://codepad.org/CaM2ViIJ, http://ideone.com/8ZJUl
при этом на самом деле причина ошибки в том, что g++ считает method_to_fn перегруженной функцией, и по этому нужен static_cast чтобы выбрать "перегрузку".

почему так?

Автор: Леопольд 27.11.2010, 15:26
Цитата(GoldFinch @  27.11.2010,  15:20 Найти цитируемый пост)
Код
(callback_t)static_cast<void(*)(Foo*)>(method_to_fn<Foo>)
Красотища...
Чистой воды UB.

Автор: boostcoder 27.11.2010, 15:32
Цитата(GoldFinch @  27.11.2010,  15:20 Найти цитируемый пост)
g++ выдаст ошибку 

хз о чем Вы...
http://liveworkspace.org/code/f7dde36deec8b68c94dc382d0b33ea77

Автор: GoldFinch 27.11.2010, 15:35
boostcoder, какая там версия гцц?

Автор: Леопольд 27.11.2010, 15:37
GoldFinch, пока ещё не смог понять из кода, что именно надо...

Автор: GoldFinch 27.11.2010, 15:38
Леопольд, не страшно

boostcoder, похоже действительно в 4.5 это починили, это какая-то особенность С++0х 

Автор: boostcoder 27.11.2010, 15:41
Цитата(GoldFinch @  27.11.2010,  15:35 Найти цитируемый пост)
какая там версия гцц? 

4.5.1

Автор: Леопольд 27.11.2010, 15:42
Цитата(GoldFinch @  27.11.2010,  15:38 Найти цитируемый пост)
Леопольд, не страшно
Может это?
Код
template<typename T>
void function_with_callback(void(*callback)(T*), T * param)
{
    callback(param);
}

template<class T>
void method_to_fn(T* that)
{
    that->bar();
}

#include <iostream>

struct Foo
{
    void bar() { std::cout << "Foo::bar OK\n"; }
};

void foo(char const* str)
{
   std::cout << str << '\n';
}

int main()
{
    Foo inst;
    function_with_callback(method_to_fn<Foo>, &inst);
    function_with_callback(foo, "hello!");
}
http://liveworkspace.org/code/a442dfbbd883cfe0d08df7c6a98cd0db

Автор: GoldFinch 27.11.2010, 15:44
Леопольд, не угадали.

Автор: Леопольд 27.11.2010, 15:49
Цитата(GoldFinch @  27.11.2010,  15:44 Найти цитируемый пост)
Леопольд, не угадали. 
Тогда, может объясните по человечески? Потому что, если избавиться от UB, получается именно это...

Автор: GoldFinch 27.11.2010, 15:51
Цитата(boostcoder @ 27.11.2010,  16:32)
Цитата(GoldFinch @  27.11.2010,  15:20 Найти цитируемый пост)
g++ выдаст ошибку 

хз о чем Вы...
http://liveworkspace.org/code/f7dde36deec8b68c94dc382d0b33ea77

эээ нееет) это чит smile
потому что когда там обе строчки, версия со статик-кастом позволяет работать версии без статик-каста
Код

typedef void(*callback_t)(void*);
void function_with_callback(callback_t callback, void* param)
{
    callback(param);
}
template<class T>
void method_to_fn(T* that)
{
    that->bar();
}

#define CODE_VARIANT 1

#include <iostream>
struct Foo
{
    void bar() { std::cout << "Foo::bar OK\n"; }
};
int main()
{
#if CODE_VARIANT == 1
    function_with_callback((callback_t)method_to_fn<Foo>, NULL);
#else
    function_with_callback((callback_t)static_cast<void(*)(Foo*)>(method_to_fn<Foo>), NULL);
#endif
}


http://liveworkspace.org/code/9b7c03115be3f8a042fc50ee142cd60e

Добавлено через 2 минуты и 8 секунд
Леопольд, this тут вообще ни при чем, а нулевой this - это вроде не UB.
В любом случае, это к проблеме отношения не имеет, добавляйте туда объект если хотите.

Добавлено через 4 минуты и 8 секунд
btw, liveworkspace.org всем хорош, только там табуляция 3 символа =\ хотелось бы 4 %)

Автор: Леопольд 27.11.2010, 16:00
Цитата(GoldFinch @  27.11.2010,  15:51 Найти цитируемый пост)
Леопольд, this тут вообще ни при чем, а нулевой this - это вроде не UB.
Показалось... Там ведь не разыменовывается указатель.

UB приводить указатели на функцию, static_cast неспроста не даёт этого делать.
Это UB в действии http://liveworkspace.org/code/9b7c03115be3f8a042fc50ee142cd60e

Использовать С-ишное приведение в С++, зачем?
Код
#include <cstdlib>

typedef void(*callback_t)(void*);
void function_with_callback(callback_t callback, void* param)
{
    callback(param);
}

template<class T>
void method_to_fn(void * param)
{
    static_cast<T*>(param)->bar();
}

#define CODE_VARIANT 2

#include <iostream>
struct Foo
{
    void bar() { std::cout << "Foo::bar OK\n"; }
};
int main()
{
    function_with_callback(method_to_fn<Foo>, NULL);
}

Наверное, это ближе всех:
http://liveworkspace.org/code/c631f5ef9be4965cd477373840e60b9a
static_cast из void * , тоже самое что и reinterpret_cast. Только выглядит безобиднее.

На мой взгляд, не особо много смысла. Зачем это надо?

Автор: GoldFinch 27.11.2010, 16:21
Леопольд, С-style cast тоже ни при чем. Что вы, приведения типов функций никогда чтоле не видели %)

Автор: Леопольд 27.11.2010, 16:21
Цитата(GoldFinch @  27.11.2010,  16:21 Найти цитируемый пост)
Что вы, приведения типов функций никогда чтоле не видели
В С++ не видел. Про С не могу сказать, может там это легально.

Автор: GoldFinch 27.11.2010, 16:28
Леопольд, впрочем, спасибо за подсказку

убрал из сигнатуры method_to_fn шаблонный тип, "перегрузки" исчезли, и всё заработало

Код

typedef void(*callback_t)(void*);
void function_with_callback(callback_t callback, void* param)
{
    callback(param);
}
template<class T>
void method_to_fn(void* that)
{
    static_cast<T*>(that)->bar();
}

#include <iostream>
struct Foo
{
    void bar() { std::cout << "test OK\n"; }
};
int main()
{
    function_with_callback(method_to_fn<Foo>, NULL);
}


Автор: Леопольд 27.11.2010, 16:42
Код
typedef void(*callback_t)(void*);
void function_with_callback(callback_t callback, void* param)
{
    callback(param);
}

template<class T>
void method_to_fn(void* that)
{
    static_cast<T*>(that)->bar();
}

#include <iostream>
struct Bar
{
   int field;  
} inst;

struct Foo
{
   int field; 
    void bar() { std::cout << "test OK\n"; field = 1234; }
};

int main()
{
    function_with_callback(method_to_fn<Foo>, &inst);
    std::cout << inst.field <<std::endl;
    
    return 0;
}
Ни одного ворнинга.
http://liveworkspace.org/code/bf8a2fa6241c39644c42f3af3acb7ab2

Автор: GoldFinch 27.11.2010, 16:47
Леопольд, помоему я написал тожесамое

Добавлено через 7 минут и 57 секунд
Всем спасибо за участие, вопрос решен.


---------

собственно ради чего это всё затевалось:
Код

//------- Third-party library code ------

typedef void(*callback_t)(void*);
void function_with_callback(callback_t callback, void* param)
{
    callback(param);
}

//------- Our library code --------

template<class T, void (T::*m)()>
void method_to_fn(void* that)
{
    (static_cast<T*>(that)->*m)();
}

//------- test ------

#include <iostream>

int main()
{
    struct Foo
    {
        void bar() { std::cout << "Foo::bar OK\n"; }
    };

    Foo foo;
    
    function_with_callback(method_to_fn<Foo, &Foo::bar>, &foo);
}


Автор: mes 27.11.2010, 19:10
Цитата(GoldFinch @  27.11.2010,  15:47 Найти цитируемый пост)
собственно ради чего это всё затевалось:

угу.. нажить проблемы :
http://liveworkspace.org/code/5b05551a3ef03138d3e10f277664cf41

Автор: Леопольд 27.11.2010, 19:56
Цитата(GoldFinch @  27.11.2010,  16:47 Найти цитируемый пост)
Леопольд, помоему я написал тожесамое
Не совсем, я показал что не так с static_cast ( mes показал тоже самое). Можно легко получить UB, компилятор ничем не сможет помочь.
вот ещё UB, компилятор проглотил не поперхнувшись
Код
//------- Third-party library code ------
typedef void(*callback_t)(void*);
void function_with_callback(callback_t callback, void* param)
{
    callback(param);
}
//------- Our library code --------
template<class T, void (T::*m)()>
void method_to_fn(void* that)
{
    (static_cast<T*>(that)->*m)();
}
//------- test ------

#include <iostream>

int main()
{
    struct Foo
    {
        int i;
        void bar() { std::cout << "Foo::bar OK\n"; reinterpret_cast<std::string&>(i) = "Foo1::bar FALSE\n"; }
    };

    struct Foo1
    {
        std::string s;
        void bar(void) { std::cout << s; }
        Foo1() : s("Foo1::bar OK\n") {}
    };

    Foo1 foo;
    
    function_with_callback(method_to_fn<Foo, &Foo::bar>, &foo);

    foo.bar();
}
http://liveworkspace.org/code/43f2050c7444c9c80504d1240d20c1f3

Надо написать шаbлон оbёртку, который включит типизацию оbратно.

Автор: GoldFinch 27.11.2010, 23:15
mes, зачем было городить такой сложный код, передали бы туда сразу int* например, оно бы вообще упало %)

предложите тогда лучший код, чтоб работал в гцц

Автор: mes 27.11.2010, 23:25
Цитата(GoldFinch @  27.11.2010,  22:15 Найти цитируемый пост)
mes, зачем было городить такой сложный код, передали бы туда сразу int* например, оно бы вообще упало %)

так в этом случае Вы могли бы свалить на ССЗБ.. а тут вы вроде и метод правильно получили... да не тот.. smile

Добавлено через 3 минуты и 20 секунд
единственный безопасный способ получить void*_адрес метода.. это взять его у статической прокси функции..
(т.е. без лишних телодвижений  увы никак)..

Автор: GoldFinch 27.11.2010, 23:30
поясню, этот код нужен чтобы передавать коллбэки на С++ в библиотеки с Си-интерфейсом.
естественно у function_with_callback будет в сигнатуре void() и void* и на каком-то этапе надо будет привести T* в void*

кстати пожалуй более интересный вариант это
Код

//------- Third-party library code ------
typedef void(*callback_t)(void*);
void function_with_callback(callback_t callback, void* param)
{
    callback(param);
}
//------- Our library code --------
template<class T>
void ftor_to_fn(void* that)
{
    (*(T*)that)();
}
//------- test ------
#include <iostream>
#include <boost/bind.hpp>
int main()
{
    struct Foo
    {
        void operator()() { std::cout << "test #1 OK\n"; }
        void bar() { std::cout << "test #2 OK\n"; }
        void baz() { std::cout << "test #3 OK\n"; }
    };
    Foo foo;
    
    function_with_callback(ftor_to_fn<Foo>, &foo);

    auto f1 = boost::bind(&Foo::bar, &foo);
    function_with_callback(ftor_to_fn<decltype(f1)>, &f1);
    
    auto f2 = [&foo](){ foo.baz(); };
    function_with_callback(ftor_to_fn<decltype(f2)>, &f2);
}


Добавлено через 4 минуты и 54 секунды
mes, я не понимаю. 
вы написали 
Код

function_with_callback(method_to_fn<Foo, &Foo::bar>, &afoo); // тип afoo != Foo

вторым аргументом function_with_callback может быть чтоугодно, 
по этому подразумевается что пользователь должен сам убедиться что если 1й аргумент это адаптер для T, то 2й аргумент имеет тип T*

Автор: mes 27.11.2010, 23:42
Цитата(GoldFinch @  27.11.2010,  22:30 Найти цитируемый пост)
что пользователь должен сам убедиться что если 1й аргумент это адаптер для T, то 2й аргумент имеет тип T*

в таком случае у Вас код не будет принимать для наследника, методы определенные в предке.. 
еще хуже - руки связаны..

Добавлено через 1 минуту и 30 секунд
Цитата(GoldFinch @  27.11.2010,  22:30 Найти цитируемый пост)
кстати пожалуй более интересный вариант это

этот уже похож на нормальный.. нужно внимательней посмотреть.. 

Автор: GoldFinch 27.11.2010, 23:51
вот тут вот надо обязательно использовать дополнительную переменную
Цитата(GoldFinch @  28.11.2010,  00:30 Найти цитируемый пост)
    auto f2 = [&foo](){ foo.baz(); };
    function_with_callback(ftor_to_fn<decltype(f2)>, &f2);

этим можно воспользоваться, и написать вот так вот:

Код

//------- Third-party library code ------
typedef void(*callback_t)(void*);
void function_with_callback(callback_t callback, void* param)
{
    callback(param);
}
//------- Our library code --------
#include <utility>
template<class Functor>
std::pair<void(*)(void*), void*> ftor_to_fn_pair(Functor&& ftor)
{
    struct Wrap
    {
        static void invoke(void* that)
        {
            (*(Functor*)that)();
        }
    };    
    return std::pair<void(*)(void*), void*>(&Wrap::invoke, &ftor);
}
//------- test ------
#include <iostream>
int main()
{
    struct Foo
    {
        void bar() { std::cout << "test OK, x = " << x << "\n"; }
        int x;
        Foo(int x) : x(x) {}
    };
    Foo foo(42);
   
    auto f = ftor_to_fn_pair([&foo](){ foo.bar(); });
    function_with_callback(f.first, f.second);
}

вроде это наиболее типобезопасный вариант

Добавлено через 3 минуты и 24 секунды
интересно правда как этот код вообще работает %)
я не очень понимаю что есть rvalue-reference, и почему значение этой лямбды продолжает жить %)

Автор: mes 27.11.2010, 23:57
Цитата(GoldFinch @  27.11.2010,  22:51 Найти цитируемый пост)
вроде это наиболее типобезопасный вариант

мне кажется тут еще можно подумать..

Добавлено через 1 минуту и 7 секунд
идти не в сторону получения пары, а предоставлении обертки над  function_with_callback

Автор: GoldFinch 28.11.2010, 00:05
... и да, использование дополнительной локальной переменной делает всё совершенно неюзабельным =\
проблема в том, что она должна где-то жить, чтобы указатель на нее был валидным.

а типичный пример использования это
Код

    Foo* foo = get_Foo_instance();

    void set_handler(void(*)(void*), void*);
    set_handler(adapt_method_to_fn(&Foo::method), foo);



Добавлено через 1 минуту и 18 секунд
пара полезна тем что она гарантирует то, что оба агрумента взяты из одного источника, который может проверить их совместимость

Добавлено через 4 минуты и 26 секунд
Цитата(mes @  28.11.2010,  00:57 Найти цитируемый пост)
предоставлении обертки над  function_with_callback 

это наиболее очевидный и понятный вариант, но мы его не рассматриваем.

мне интересно создание одной реюзабельной функции которая позволила бы делать обертки для методов

Добавлено через 12 минут и 11 секунд
более того, есть случаи, когда написание обертки практически невозможно
Код

void fn(void(*callback)(void*), void* param, const char* format, ... /*ellipsis*/);

Автор: mes 28.11.2010, 00:26
Цитата(GoldFinch @  27.11.2010,  23:05 Найти цитируемый пост)
мне интересно создание одной реюзабельной функции которая позволила бы делать обертки для методов

ну а что мешает сделать это проксю единой для всех подходящих функций ?
 smile

Добавлено @ 00:28
Цитата(GoldFinch @  27.11.2010,  23:05 Найти цитируемый пост)
более того, есть случаи, когда написание обертки практически невозможно

ну еллипсис впринципе можно тоже разгрузить..

другой вопрос - нужно ли это..

Автор: mes 28.11.2010, 00:54
вот примерно о чем я предлагал  (на основе вашего кода)
Код

template <typename F1, typename F2>
void proxy (F1 && f1, F2&&  f2)
{
    struct Wrap
    {
        static void invoke(void* that)
        {
            (*(F2*)that)();
        }
    }; 
    f1 (&Wrap::invoke, &f2);
}

 Foo foo(42);
    
 proxy ( std::bind(fcb,_1,_2) 
       , std::bind(&Foo::bar,&foo) );


fcb - function_with_callback

Автор: Леопольд 28.11.2010, 00:59
mes, думается, это надо для реального проекта. Навряд ли rvalue ссылки можно в нём использовать. Правда, ничто не мешает из заменить на оbычные... smile

Автор: mes 28.11.2010, 01:02
Цитата(Леопольд @  27.11.2010,  23:59 Найти цитируемый пост)
Навряд ли rvalue 

я их у тс перенял smile раз ему можно , значит все в порядке  smile

Добавлено @ 01:03
тем более они к теме напрямую не относятся..
и вопрос о надежности уже был также поднят тс.. но я например не в состоянии на него ответить.. поэтому пока беру как есть smile

Добавлено @ 01:04
суть в самом вызове proxy, чтоб не приходилось в ручную заниматься "подбором" параметров.. 

Автор: GoldFinch 28.11.2010, 01:06
не, в реальной ситуации у function_with_callback будет еще куча параметров, так что никакой реюзабельности не получится

Автор: mes 28.11.2010, 01:56
Цитата(GoldFinch @  28.11.2010,  00:06 Найти цитируемый пост)
не, в реальной ситуации у function_with_callback будет еще куча параметров, так что никакой реюзабельности не получится 

ну да, а bind тогда нам зачем ?!  smile 

допустим fcb имеет такой вид :
Код

typedef void(*callback_t)(void*);
void fcb (int i, float f, void* param, callback_t callback, int j)
{
    callback(param);
}

тогда вызов может иметь такой вид :
Код

  proxy ( std::bind(fcb, 4, 6.3, _2, _1, 8 ) 
          , std::bind(&Foo::bar,&foo) );

где 4,6.3,8 значения параметров, которые мы должны передать функции помимо колбяка.. 
 smile 

Автор: GoldFinch 28.11.2010, 02:23
м.. ну да.. только выглядит ужасно  =\

Автор: mes 28.11.2010, 02:40
Цитата(GoldFinch @  28.11.2010,  01:23 Найти цитируемый пост)
м.. ну да.. только выглядит ужасно  =\ 

..
Код

 auto f = ftor_to_fn_pair(bind (&Foo::bar, &foo) );
 fcb (4, 6.3, f.second, f.first, 8);

Код

 proxy ( std::bind(fcb, 4, 6.3, _2, _1, 8 ) 
       , std::bind(&Foo::bar,&foo) );

ну не знаю какой из кодов выглядит ужасней 
smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)