Нетипичный параметр шаблона ... это шаблон! (C ++) - PullRequest
6 голосов
/ 15 июля 2009

Я в основном ищу генерацию оболочки для универсальной функции C без необходимости вручную указывать типы. Итак, у меня есть обратный вызов с фиксированным прототипом, но мне нужно будет сделать какой-то специальный код в обертке, основанный на типе обернутой функции ... Так что в основном я думаю об использовании статического метода в шаблоне класса чтобы обернуть мою функцию в соответствующий интерфейс, например:

// this is what we want the wrapped function to look like
typedef void (*callback)(int); 
void foobar( float x ); // wrappee

// doesn't compile
template< T (*f)(S) > // non-type template param, it's a function ptr
struct Wrapper
{
  static void wrapped(int x)
  {
     // do a bunch of other stuff here
     f(static_cast<S>(x)); // call wrapped function, ignore result

  }
}

А потом я хотел бы сделать что-то вроде:

AddCallback( Wrapper<foobar>::wrapped );

Однако проблема в том, что я не могу просто пойти дальше и использовать «S» в параметре функции в шаблоне Wrapper, я должен сначала перечислить его как параметр:

template< class T, class S, T (*f)(S) >
struct Wrapper
// ...

Но это означает, что гораздо труднее использовать (Wrapper<void,float,foobar>::wrapped), в идеале я хотел бы просто передать туда указатель функции и заставить его автоматически обрабатывать типы параметров (и возвращаемые типы). Чтобы было ясно, внутри обернутой функции мне нужно обратиться к типам указателя на функцию (поэтому мне нужен некоторый эквивалент S или T).

Есть ли способ сделать это?

Ответы [ 4 ]

5 голосов
/ 15 июля 2009

Одна вещь, которую вы, возможно, пожелаете рассмотреть, - это использование LLVM или чего-либо подобного для создания соответствующей функции батута во время выполнения. Или вот статическое решение:

#include <iostream>

void f(float f) { std::cout << f << std::endl; }

template<typename T, typename S> struct static_function_adapter {
        template<T(*f)(S)> struct adapt_container {
                static void callback(int v) {
                        f(static_cast<S>(v));
                }
        };

        template<T(*f)(S)> adapt_container<f> adapt() const {
                return adapt_container<f>();
        }
};

template<typename T, typename S> struct static_function_adapter<T, S> get_adapter(T (*)(S)) {
        return static_function_adapter<T, S>();
}

#define ADAPTED_FUNCTION(f) (&get_adapter(f).adapt<f>().callback)

int main() {
        void (*adapted)(int) = ADAPTED_FUNCTION(f);
        adapted(42);
        return 0;
}

Функция get_adapter позволяет нам выводить аргумент и возвращаемый тип; adapt () затем преобразует это в тип, параметризованный для фактической функции, и, наконец, мы получаем статическую функцию в обратном вызове.

0 голосов
/ 15 июля 2009

РЕДАКТИРОВАТЬ: совершенно новый ответ

Хорошо, я полностью переосмыслил вопрос и считаю, что я получаю то, что вы хотите. Я действительно делал это раньше: -P.

Вот идея, у меня есть базовый класс, который перегружает operator (), тогда у меня есть подкласс для каждой «арности» функций. Наконец, у меня есть фабричная функция, которая возвращает одну из этих вещей. Код большой (и, возможно, немного излишний), но работает хорошо. Большая часть перегрузок library_function предназначена для поддержки различных синтаксисов, в основном ненужных. Он также поддерживает boost::bind функции, функции-члены и т. Д., Гораздо больше, чем вам, вероятно, нужно.

http://pastebin.com/m35af190

Пример, использование:

// map of library functions which will return an int.
std::map<std::string, LibraryFunction<int> > functions;

// function to register stuff in the map
void registerFunction(const std::string &name, LibraryFunction<int> func) {
    functions.insert(std::make_pair(name, func));
}

позже вы можете сделать это:

// the this param is so the function has access to the scripting engine and can pop off the parameters, you can easily chop it out

// register 2 functions, one with no params, one with 1 param
registerFunction("my_function", library_function1(*this, call_my_function));
registerFunction("my_function2", library_function0(*this, call_my_function2));

functions["my_function"]();
functions["my_function2"]();
0 голосов
/ 15 июля 2009

Я бы посмотрел на повышение. При первом прочтении вашего вопроса мне кажется, что <boost/function_types/parameter_types.hpp> обеспечивает то, что вам нужно.

0 голосов
/ 15 июля 2009

Если вы используете функцию, которая возвращает «завернутый», а не ссылается на него напрямую, компилятор попытается автоматически сопоставить параметры шаблона для вызова функции.

edit: А как насчет этого?

int foobar( float x ); // wrappee

template <typename T, typename S>
struct Wrapper {
    typedef T (*F)(S);
    F f;

    Wrapper(F f) : f(f) { }

    void wrapped(S x) {
        // do a bunch of other stuff here
        f(x); // call wrapped function, ignore result
    }
};

template <typename T, typename S>
Wrapper<T,S> getWrapper(T (*f)(S)) {
    return Wrapper<T,S>(f);
}

...
getWrapper(foobar).wrapped(7);
...