Невозможно передать нулевой указатель на функцию в качестве аргумента шаблона - PullRequest
9 голосов
/ 09 июля 2011

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

#include <iostream>
struct Foo {
    int i;
};
template <typename T>
T* T_new() {
    return new T();
}
Foo* Foo_new() {
    return new Foo();
}
template <typename T, T* (*func)()>
T* T_new() {
    if (func)
        return func();
    else
        return NULL;
}

int main(void) {
    // Works
    Foo* f1 = T_new<Foo>();
    std::cout << f1 << std::endl;

    // Works
    Foo* f2 = T_new<Foo, Foo_new>();
    std::cout << f2 << std::endl;

    // fails to compile, "no matching function for call to ‘T_new()’"
    // Foo* f3 = T_new<Foo, NULL>();
    // std::cout << f3 << std::endl;

    return 0;
}

Я нашел этот похожий вопрос, но он касается передачи нуля в качестве аргумента конструктору, а не передачи нуля в качестве аргумента шаблона, и хитрость там (с использованием (Foo*)0) не работает в качестве аргумента шаблона.

Есть ли способ обойти это или сделать какую-то хитрую специализацию шаблона или какую-нибудь другую хитрую вещь, чтобы получить желаемый эффект?

EDIT:

Выше был упрощенный пример, который иллюстрировал проблему, с которой я столкнулся, но вот конкретная проблема, которую я пытаюсь решить. У меня есть этот проект Я работаю над этим. Это набор функций, которые упрощают мне смешивание C ++ и Lua (по разным причинам я не хочу использовать LuaBind или другие существующие функции, которые я там обнаружил). Важная функция в этом вопросе - luaW_register<T> около дна. Это немного устаревшая версия, но она работает практически во всех случаях. Это не работает, однако, если конструктор является закрытым, что произошло, когда я попытался смешать это с Box2D b2Body (который должен быть сделан из b2World). luaW_defaultallocator<T>()luaW_defaultdeallocator<T>()) все еще создается, поскольку я использую его в качестве аргумента по умолчанию в luaW_register<T>().

Мое предлагаемое решение состояло в том, чтобы вытянуть параметр allocator в параметры шаблона luaW_Register. Затем, если я хочу использовать какую-то другую функцию для получения моих объектов для определенного типа, luaW_defaultallocator даже не будет создан. В таких случаях, как b2Body s, когда они вообще не могут создать себя, я хотел бы иметь возможность просто передать NULL в качестве аргумента шаблона (что кажется вполне разумным, но компилятор задыхается от него по причинам это все еще неясно для меня, кажется, что я могу установить значение в NULL где-нибудь еще в коде, который я должен быть в состоянии также для шаблонов). Хак, который я первоначально реализовал, заключался в передаче логического аргумента моей функции, который отключил бы возможность вызова Foo.new из моего кода Lua, но это не остановило бы defaultallocator от компиляции, и если бы я мог использовать проверку нуля и работать так, как мне хотелось бы, имеет приятный побочный эффект: я просто проверяю, есть ли распределитель, и использую его для управления добавлением функции new в таблицу lua.

tl; dr: моя цель состояла в том, чтобы перейти от этого:

template <typename T>
void luaW_register(lua_State* L, const char* classname, const luaL_reg* table, const luaL_reg* metatable, const char** extends = NULL, bool disablenew = false, T* (*allocator)() = luaW_defaultallocator<T>, void (*deallocator)(T*) = luaW_defaultdeallocator<T>)

на это:

template <typename T, T* (*allocator)() = luaW_defaultallocator<T>, void (*deallocator)(T*) = luaW_defaultdeallocator<T> >
void luaW_register(lua_State* L, const char* classname, const luaL_reg* table, const luaL_reg* metatable, const char** extends = NULL)

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

Наиболее близким решением, которое я видел до сих пор, является предоставление функции, подобной luaW_cannotalloc<T>(lua_State*), которая возвращает NULL и может быть проверена в моей функции luaW_register вместо null. Я полагаю, это сработает, но это означает, что нужно больше набирать и запоминать это имя функции, а NULL кажется намного чище.

Ответы [ 5 ]

3 голосов
/ 09 июля 2011

Эту проблему можно решить с помощью шаблонных перегрузок. Вместо того, чтобы иметь только одну подпись «T_new», у вас будет одна подпись для случая NULL и одна для другого:

// Unused signature, no implementation so using this will result in link error
template<typename T, typename F>
T* T_new();
// NULL overload (NULL is an int)
template<typename T, int func>
T* T_new()
{
    assert(func == 0 && "Signature should only be used with NULL");
    return NULL;
}
// Valid function pointer overload
template<typename T, T* (*func)()>
T* T_new()
{
    // I don´t think it´s possible with NULL functions now, but if it is
    // we'll handle that too
    if (func)
        return func();
    return NULL;
}

Хитрость заключается в том, чтобы понять, что NULL на самом деле является int, и использовать это для обработки NULL-случая при другой перегрузке.

3 голосов
/ 09 июля 2011

Проблема для нулевого указателя состоит в том, что аргумент указателя шаблона должен иметь внешнюю связь. И ноль не имеет связи.

Как заставить вещи работать: кажется, что вы выбрали неправильный инструмент для того, чего вы пытаетесь достичь.

Приветствия & hth.,

1 голос
/ 09 июля 2011

Вы можете (я думаю) установить константу соответствующего типа и использовать ее:

(Foo*)(*make_null_foo)() = 0;

Foo* f3 = T_new<Foo, make_null_foo>();

Или в C ++ 0x вы сможете использовать новое ключевое слово nullptr.

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

Foo* make_null_foo() { return 0; }

Foo* f3 = T_new<Foo, make_null_foo>();

, то естьШаблон нулевого объекта.

0 голосов
/ 09 июля 2011

Я не думаю, что шаблоны здесь имеют смысл, и я не уверен, что вы действительно продумали это ... почему бы вам статически вызвать функцию, зная, что она вернет NULL?

В любом случае вы можете сделать это:

template <typename T, T* (*func)()>
T* T_new() {
   return func();
}
template <typename T>
T* T_new() {
   return NULL;
}

Или, если вам нужно пройти через промежуточные шаблоны (т. Е. Вы можете не знать в данный момент, будет ли функция нулевой или нет, вы можете следовать шаблону нулевого объекта и предоставить нуль-функция :

template <typename T>
T* null_new() { return 0; }

template <typename T, T* (*f)() >
T* T_new() {
   return f();
}
// user code:
X* p = T_new<X, null_new >();

В качестве альтернативы, забудьте об использовании указателя функции в качестве аргумента шаблона и передайте его в качестве реального аргумента функции:

template <typename T>
T* T_new( T* (*func)() = 0 ) {
    if (func)
        return func();
    else
        return NULL;
}
0 голосов
/ 09 июля 2011

Это некрасиво, но работает:

Foo* f3 = T_new<Foo, (Foo* (*)())NULL>();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...