Ошибки неоднозначности с вариадическими шаблонами и миксинами - PullRequest
1 голос
/ 01 февраля 2012

Я пытаюсь создать класс регистрации обратного вызова, позволяющий регистрировать обратные вызовы по строковому идентификатору для различных типов. Каждый обратный вызов имеет подпись void function( T val ), где T - это изменяющийся тип.

Я создал следующий базовый класс регистратора, который отображает строки в функции.

#include <string>
#include <map>
#include <functional>
#include <cstdint>
#include <iostream>

using namespace std;

template< typename ValueType >
class BasicConfigCallbackRegistrar 
{
public:
    typedef ValueType value_type;
    typedef BasicConfigCallbackRegistrar< value_type > type;
    typedef function< void( const value_type ) > signature_type;
    typedef map< string, signature_type > callback_map_type;

    /// @brief constructor
    BasicConfigCallbackRegistrar() : callbackMap_()
    {
    }

    /// @brief register a create callback 
    /// @param nodePath the path identifying the node in config database
    /// @param callback the callback to register
    type& RegisterCallback( string nodePath, signature_type callback )
    {
        callbackMap_.insert( make_pair( move(nodePath), callback ) );
        return *this;
    }

    void MakeCallback( const string& nodePath, value_type val )
    {
        // no checking assumes item is in map,
        // do not do this in production code
        auto iter = callbackMap_.find( nodePath );
        iter->second( val );
    }

private:
    callback_map_type callbackMap_;       ///< the callback map
};

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

template< typename... Types >
class ConfigCallbackRegistrar : public BasicConfigCallbackRegistrar<Types>... 
{
public:
    /// @brief constructor
    ConfigCallbackRegistrar() : BasicConfigCallbackRegistrar<Types>()...
    {}
};

Затем этот код определяется как:

typedef ConfigCallbackRegistrar< uint32_t, string > CallbackRegistrar;

Когда я пытаюсь использовать этот класс следующим образом:

struct UintFtor
{
    void operator()( uint32_t val )
    {
        cout << val << "\n"; 
    }
};

struct StringFtor
{
    void operator ()( string val )
    {
        cout << val << "\n"; 
    }
};

int main()
{
    CallbackRegistrar registrar{};

    registrar.RegisterCallback( "SomeNode", UintFtor() );
    registrar.RegisterCallback( "SomeNode", StringFtor() );

    return 0;
}

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

variadic-wrap.cpp: In function ‘int main()’:
variadic-wrap.cpp:87: error: request for member ‘RegisterCallback’ is ambiguous
variadic-wrap.cpp:27: error: candidates are: BasicConfigCallbackRegistrar<ValueType>&  BasicConfigCallbackRegistrar<ValueType>::RegisterCallback(std::string, std::function<void(ValueType)>) [with ValueType = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]
variadic-wrap.cpp:27: error:                 BasicConfigCallbackRegistrar<ValueType>& BasicConfigCallbackRegistrar<ValueType>::RegisterCallback(std::string, std::function<void(ValueType)>) [with ValueType = unsigned int]
variadic-wrap.cpp:88: error: request for member ‘RegisterCallback’ is ambiguous
variadic-wrap.cpp:27: error: candidates are: BasicConfigCallbackRegistrar<ValueType>& BasicConfigCallbackRegistrar<ValueType>::RegisterCallback(std::string, std::function<void(ValueType)>) [with ValueType = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]
variadic-wrap.cpp:27: error:                 BasicConfigCallbackRegistrar<ValueType>& BasicConfigCallbackRegistrar<ValueType>::RegisterCallback(std::string, std::function<void(ValueType)>) [with ValueType = unsigned int]

Вызов MakeCallback, который принимает параметр типа value_type, также приводит к той же ошибке неоднозначности.

Как я могу решить эту проблему без явного приведения registrar к типу базового класса?

1 Ответ

0 голосов
/ 01 февраля 2012

У меня есть решение.Все, что нужно ТАТ - это дополнительный уровень косвенности.В классе Derived я добавил шаблонную функцию, которая отправляет правильную базу следующим образом:

template< typename... Types >
class ConfigCallbackRegistrar : public BasicConfigCallbackRegistrar<Types>...
{
    typedef ConfigCallbackRegistrar<Types...> type;
public:
    /// @brief constructor
    ConfigCallbackRegistrar() : BasicConfigCallbackRegistrar<Types>()...
    {}

    template<typename T> 
    type& Register( string nodePath, 
            typename BasicConfigCallbackRegistrar<T>::signature_type callback )
    {
        BasicConfigCallbackRegistrar<T>::RegisterCallback( std::move(nodePath), 
                std::move(callback) );
        return *this;
    } 
};

Код вызова становится:

int main()
{
    CallbackRegistrar registrar{};

    registrar.Register<uint32_t>( "SomeNode", UintFtor() );
    registrar.Register<string>( "SomeNode", StringFtor() );

    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...