I am trying to create a callback registration class tat allows registration of callbacks against string identifier for different types. Each callback has the signature void function( T val ) where T is the changing type.
I have created the following base registrar class that maps strings to functions.
#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
};
I then use variadic templates to create a derived class for each of the types I want to support.
template< typename... Types >
class ConfigCallbackRegistrar : public BasicConfigCallbackRegistrar<Types>...
{
public:
/// #brief constructor
ConfigCallbackRegistrar() : BasicConfigCallbackRegistrar<Types>()...
{}
};
This is then typedefed as:
typedef ConfigCallbackRegistrar< uint32_t, string > CallbackRegistrar;
When I try to use this class as follows:
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;
}
Unfortunately when I try to compile this I get the following ambiguity errors:
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]
Calls to MakeCallback which takes a parameter of type value_type also produce the same ambiguity error.
How can I resolve this without an explicit cast of registrar to the base class type?
I have a solution. All tat is needed is an extra level of indirection. In the Derived class I added a template function that dispatches to the correct base, as follows:
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;
}
};
The calling code becomes:
int main()
{
CallbackRegistrar registrar{};
registrar.Register<uint32_t>( "SomeNode", UintFtor() );
registrar.Register<string>( "SomeNode", StringFtor() );
return 0;
}
Related
#include <iostream>
#include <unordered_map>
#include <utility>
#include <typeinfo>
using namespace std;
class Handle{
public:
int val;
bool getAskPrice(int& tmp) const
{
tmp = val;
return true;
}
bool setAskPrice(int& tmp)
{
val = tmp;
return true;
}
};
template<class RT, class ARG>
struct convertToAFL{
static RT to_afl(ARG);
};
template<class RT, class ARG>
struct convertFromAFL{
static RT from_afl(ARG);
};
template<>
struct convertToAFL<float, int>
{
static float to_afl(int& value)
{
return static_cast<float>(value);
}
};
template<>
struct convertFromAFL<int, float>
{
static int from_afl(float& val)
{
return static_cast<int>(val);
}
};
struct Getter{
template<typename TICK_D, bool (Handle::*Getter)(TICK_D&) const, typename AFL_D>
static AFL_D getter(const Handle& handle)
{
TICK_D temp;
bool exists;
exists = (handle.*Getter)(temp);
AFL_D x = convertToAFL<AFL_D, TICK_D>::to_afl(temp);
return exists ? x : -1;
}
};
struct Setter{
template<typename TICK_D, bool (Handle::*Setter)(TICK_D&), typename AFL_D>
static void setter(Handle& handle, AFL_D& val)
{
TICK_D x;
x = convertFromAFL<TICK_D, AFL_D>::from_afl(val);
(handle.*Setter)(x);
}
};
int main()
{
Handle h;
float val = 20.0;
Setter::setter<int, &Handle::setAskPrice, float>(h, val);
std::cout<<Getter::getter<int, &Handle::getAskPrice, float>(h);
//std::pair<, &Setter::setter<int, &Handle::setAskPrice, float>> x;
return 0;
}
The above code works as expected, however, in the main() instead of calling the functions, how can I store the pointer to the templatized Setter:setter() and Getter::getter() ?
I m trying something like
std::pair<&Setter::setter<int, &Handle::setAskPrice, float>, &Getter::getter<int, &Handle::getAskPrice, float>(h)> func_pair;
And be able to call the functions later.
But i get an error saying
main.cpp: In function ‘int main()’:
main.cpp:85:118: error: type/value mismatch at argument 1 in template parameter list for ‘template struct std::pair’
std::pair<&Setter::setter<int, &Handle::setAskPrice, float>, &Getter::getter<int, &Handle::getAskPrice, float>(h)> func_pair;
^
main.cpp:85:118: note: expected a type, got ‘& setter’
main.cpp:85:118: error: template argument 2 is invalid
Static member functions are just a normal functions. You can store these pointers like this:
std::pair<void (*)(Handle& handle, float& val), float (*)(const Handle& handle)>
func_pair(&Setter::setter<int, &Handle::setAskPrice, float>, &Getter::getter<int, &Handle::getAskPrice, float>);
Your problem is that template parameter is a type, but you are passing a value (pointer) as an argument. Instead, you could use auto like this:
auto func_pair = std::make_pair(&Setter::setter<int, &Handle::setAskPrice, float>, &Getter::getter<int, &Handle::getAskPrice, float>);
Edit: if you are using C++03, std::make_pair() is still available, but not auto. You will need to describe the type manually with a series of typedefs.
You can use decltype to get the pointer type.
Example:
std::pair<int, decltype(&Setter::setter<int, &Handle::setAskPrice, float>)> x = {
1, &Setter::setter<int, &Handle::setAskPrice, float>
};
Prior to C++11:
std::pair<int, void(*)(Handle&, float&)> x(
1, &Setter::setter<int, &Handle::setAskPrice, float>
);
I was trying to separate a "generic" map from the event manager implementation, because I need to use it somewhere else. But I ran into something quite unusual for me. So it seems like I'm trying to generate two times (at least) the GetValue function.
#include <tuple>
#include <utility>
#include <vector>
#include <functional>
namespace meta {
template < typename T >
struct CType { using type = T; };
}
namespace containers {
template < template < typename T > class T_Storage,
typename... T_Keys >
class TypedMap {
template < typename T_Key >
using U_Pair = decltype(
std::make_pair(
meta::CType<T_Key>{},
T_Storage<T_Key>{}
));
using U_Map = decltype(
std::make_tuple(
U_Pair<T_Keys>{}
...));
public:
U_Map m_map;
//-----------------------------------------
//! Functions
public:
//!
//! #fn GetValue
//! #brief Acess to map[key]
//! #param type is the key used to find the data
//! #return map[key] reference
//!
template < typename T_Key >
constexpr decltype(auto) GetPair() {
return std::get<U_Pair<T_Key>>(m_map);
}
template < typename T_Key >
decltype(auto) GetValue() {
return std::get<1>(GetPair<T_Key>());
}
};
}
//!
//! #class EventManager
//!
template < typename ... T_Events >
class EventManager {
template < typename T_Event >
using U_EventCallback = std::function<void(T_Event)>;
template < typename T_Event >
using U_ListenersArray = std::vector<U_EventCallback<T_Event>>;
private:
containers::TypedMap<U_ListenersArray> m_listenersMap;
public:
EventManager() = default;
~EventManager() = default;
public:
template < typename T_Event >
decltype(auto) GetListeners() {
return m_listenersMap.template GetValue<T_Event>();
}
};
struct event1 {};
struct event2 {};
using U_EventManager = EventManager<event1,event2>;
int main() {
U_EventManager test;
auto result = test.GetListeners<event1>();
}
Wandbox
UPDATE:
So the error was generated because of a typo... But still I'd like to understand how this error occured, to be able to understand it next time.
In file included from prog.cc:1: /opt/wandbox/clang-head/include/c++/v1/tuple:1018:5: error: static_assert failed due to requirement '!is_same<pair<CType<event1>, vector<function<void (event1)>, allocator<function<void (event1)> > >
>, pair<CType<event1>, vector<function<void (event1)>, allocator<function<void (event1)> > > > >::value' "type not in empty type list"
static_assert(!is_same<_T1, _T1>::value, "type not in empty type list");
^ ~~~~~~~~~~~~~~~~~~~~~~~~~ /opt/wandbox/clang-head/include/c++/v1/tuple:1025:14: note: in instantiation of template class 'std::__1::__find_detail::__find_exactly_one_checked<std::__1::pair<meta::CType<event1>, std::__1::vector<std::__1::function<void (event1)>, std::__1::allocator<std::__1::function<void (event1)> > > >>' requested here
: public __find_detail::__find_exactly_one_checked<_T1, _Args...> {
^ /opt/wandbox/clang-head/include/c++/v1/tuple:1032:23: note: in instantiation of template class 'std::__1::__find_exactly_one_t<std::__1::pair<meta::CType<event1>, std::__1::vector<std::__1::function<void (event1)>, std::__1::allocator<std::__1::function<void (event1)> > > >>' requested here
return _VSTD::get<__find_exactly_one_t<_T1, _Args...>::value>(__tup);
^ prog.cc:45:29: note: in instantiation of function template specialization 'std::__1::get<std::__1::pair<meta::CType<event1>, std::__1::vector<std::__1::function<void (event1)>, std::__1::allocator<std::__1::function<void (event1)> > > >>' requested here
return std::get<U_Pair<T_Key>>(m_map);
^ prog.cc:51:36: note: in instantiation of function template specialization 'containers::TypedMap<U_ListenersArray>::GetPair<event1>' requested here
return std::get<1>(GetPair<T_Key>());
^ prog.cc:79:40: note: in instantiation of function template specialization 'containers::TypedMap<U_ListenersArray>::GetValue<event1>' requested here
return m_listenersMap.template GetValue<T_Event>();
^ prog.cc:92:24: note: in instantiation of function template specialization 'EventManager<event1, event2>::GetListeners<event1>' requested here
auto result = test.GetListeners<event1>();
^ 1 error generated.
You forgot to provide the content for your containers:
template < typename ... T_Events >
class EventManager {
/* ... */
private:
containers::TypedMap<U_ListenersArray> m_listenersMap; // <-- this line, you forgot the type list.
/* ... */
};
The marked line should be:
containers::TypedMap<U_ListenersArray, T_Events...> m_listenersMap;
Not sure... but it seems to me you've forgotten to use T_Events... in EventManager.
I mean... instead of
containers::TypedMap<U_ListenersArray> m_listenersMap;
you should, I suppose, write
containers::TypedMap<U_ListenersArray, T_Events...> m_listenersMap;
I'm trying to do the following (only relevant parts of code below):
template<typename ContainerType>
struct IsContainerCheck : is_container<ContainerType>
{
static constexpr char* err_value = "Type is not a container model";
};
namespace _check_concept {
template<typename ResultType>
struct run {
constexpr static int apply() {
static_assert(false, IsContainerCheck<ResultType>::err_value)
return 0;
}
};
template<>
struct run<true_t> {
constexpr static int apply() {
return 0;
}
};
}
This fails because the static_assert allows only literals to be printed. The same is with BOOST_STATIC_ASSERT_MSG macro.
So my question is - is there any way to output a constexpr string during compilation?
If there is a gcc extension providing this functionality that would also be great.
Used compiler gcc 4.8.1
GCC does not provide such a mechanism as you want. However you will not need
it if you are able to refactor your code somewhat as illustrated in the
following program. (I have filled in a few gaps so as to give us a
compilable example):
#include <type_traits>
#include <vector>
template<typename ContainerType>
struct is_container
{
static bool const value = false;
};
template<>
struct is_container<std::vector<int>>
{
static bool const value = true;
};
template<typename ContainerType>
struct IsContainerCheck // : is_container<ContainerType> <- Uneccessary
{
static_assert(is_container<ContainerType>::value,
"Type is not a container model");
};
namespace _check_concept {
template<typename ResultType>
struct run {
constexpr static int apply() {
return (IsContainerCheck<ResultType>(),0);
}
};
// No such specialization is necessary. Delete it.
// template<>
// struct run<true_t> {
// constexpr static int apply() {
// return 0;
// }
//};
}
using namespace _check_concept;
int main(int argc, char **argv)
{
auto verdict0 = run<std::vector<int>>::apply();
(void)verdict0;
// The following line will static_assert: "Type is not a container model"
auto verdict1 = run<float>::apply();
(void)verdict1;
return 0;
}
In your specialization _check_concept::struct run<true_t> I presume that
true_t is not an alias or equivalent of std::true_type, but rather
just a place-holder for some ResultType that is a container type. As
the test program shows, no such specialization is now necessary, because
IsContainerCheck<ResultType>() will static_assert, or not, depending
on ResultType, in the unspecialized run<ResultType>::apply().
I had some time (and a good liqueur to come along with it) to think more about the problem. This is what I came up with:
namespace _details {
struct PassedCheck {
constexpr static int printError () {
return 0; //no error concept check passed
}
};
template<template<typename> class ConceptCheck, typename ...ModelTypes>
struct check_concept_impl;
template<template<typename> class ConceptCheck, typename FirstType, typename ...ModelTypes>
struct check_concept_impl<ConceptCheck, FirstType, ModelTypes...> : mpl::eval_if< typename ConceptCheck<FirstType>::type,
check_concept_impl<ConceptCheck, ModelTypes...>,
mpl::identity<ConceptCheck<FirstType>>>
{ };
template<template<typename> class ConceptCheck, typename LastType>
struct check_concept_impl<ConceptCheck, LastType> : mpl::eval_if<typename ConceptCheck<LastType>::type,
mpl::identity<PassedCheck>,
mpl::identity<ConceptCheck<LastType>>>
{ };
}
template<template<typename> class ConceptCheck, typename ...ModelTypes>
struct check_concept {
private:
typedef typename _details::check_concept_impl<ConceptCheck, ModelTypes...>::type result_type;
public:
// the constexpr method assert produces shorter, fixed depth (2) error messages than a nesting assert in the trait solution
// the error message is not trahsed with the stack of variadic template recursion
constexpr static int apply() {
return result_type::printError();
}
};
template<typename ContainerType>
struct IsContainerCheck : is_container<ContainerType>
{
template<typename BoolType = false_t>
constexpr static int printError () {
static_assert(BoolType::value, "Type is not a container model");
return 0;
}
};
and the usage:
check_concept<IsContainerCheck, std::vector<int>, std::vector<int>, float, int>::apply();
The solution is probably not the most elegant one but I it keeps the assert message short:
In file included from ../main.cpp:4:0:
../constraint.check.hpp: In instantiation of ‘static constexpr int IsContainerCheck::printError() [with BoolType = std::integral_constant; ContainerType = float]’:
../constraint.check.hpp:61:34: required from ‘static constexpr int check_concept::apply() [with ConceptCheck = IsContainerCheck; ModelTypes = {std::vector >, std::vector >, float, int}]’
../main.cpp:25:83: required from here
../constraint.check.hpp:74:3: error: static assertion failed: Type is not a container model
static_assert(BoolType::value, "Type is not a container model");
The assert is issued in a constexpr method after the check_concept template specialization has been done. Embedding the static assert directly into the template class definition would drag the whole check_concept_impl recursion stack into the error message.
So changing the IsContainerCheck trait to something like (rest of the changes omitted for readibility):
template<typename ContainerType>
struct IsContainerCheck
{
static_assert(is_container<ContainerType>::type::value, "Type is not a container model");
};
would yield an error
../constraint.check.hpp: In instantiation of ‘struct IsContainerCheck’:
../constraint.check.hpp:36:9: required from ‘struct _details::check_concept_impl’
/usr/include/boost/mpl/eval_if.hpp:38:31: required from ‘struct boost::mpl::eval_if, _details::check_concept_impl, boost::mpl::identity > > >’
../constraint.check.hpp:36:9: required from ‘struct _details::check_concept_impl >, float, int>’
/usr/include/boost/mpl/eval_if.hpp:38:31: required from ‘struct boost::mpl::eval_if, _details::check_concept_impl >, float, int>, boost::mpl::identity > > >’
../constraint.check.hpp:36:9: required from ‘struct _details::check_concept_impl >, std::vector >, float, int>’
../constraint.check.hpp:53:84: required from ‘struct check_concept >, std::vector >, float, int>’
../main.cpp:25:81: required from here
../constraint.check.hpp:72:2: error: static assertion failed: Type is not a container model
static_assert(is_container::type::value, "Type is not a container model");
As you can see each recursive eval_if call is emended in the error description which is bad because it makes the error message dependent from the amount and type of template parameters.
I am having a problem in some code using type_traits from boost.
It is quite a complex part of the code, but I could isolate the part that gives the compilation error:
template<const size_t maxLen>
class MyString {
public:
typedef boost::conditional<(maxLen > 0), char[maxLen+1], std::string> ObjExternal;
};
template <class T>
class APIBase {
public:
typedef T obj_type;
typedef typename T::ObjExternal return_type;
};
template <class T>
int edit(const T& field, const typename T::return_type& value)
{
return 0;
}
int myFunction()
{
APIBase<MyString<10> > b;
char c[11];
return edit(b, c);
}
This gives the following error:
test.cpp: In function ‘int myFunction()’:
tes.cpp:109: error: no matching function for call to ‘edit(APIBase >&, char [11])’
tes.cpp:100: note: candidates are: int edit(const T&, const typename T::return_type&) [with T = APIBase >]
However, if I change the line with the code
char c[11];
by
MyString<10>::ObjExternal c;
it works. Similarly, if instead I change the line
typedef boost::conditional<(maxLen > 0), char[maxLen+1], std::string> ObjExternal;
by
typedef char ObjExternal[maxLen+1];
it also works. I am thinking that it is a problem with boost::conditional, as it seems it is not being evaluated right. Is there a problem in my code, or there is an alternative that can be used instead of boost::conditional to have this functionality?
I am thinking about using the 2nd option, but then I could not use maxLen as 0.
You need to use the member typedef type provided by conditional and not the conditional type itself.
Change:
typedef boost::conditional<(maxLen > 0),
char[maxLen+1],
std::string> ObjExternal;
to:
typedef typename boost::conditional<(maxLen > 0),
char[maxLen+1],
std::string>::type ObjExternal;
This first piece has been solved by Eric's comments below but has led onto a secondary issue that I describe after the horizontal rule. Thanks Eric!
I'm trying to pass a functor that is a templated class to the create_thread method of boost thread_group class along with two parameters to the functor. However I can't seem to get beyond my current compile error. With the below code:
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/thread.hpp>
#include <vector>
using namespace boost::lambda;
using namespace std;
namespace bl = boost::lambda;
template<typename ftor, typename data>
class Foo
{
public:
explicit Foo()
{
}
void doFtor ()
{
_threads.create_thread(bind(&Foo<ftor, data>::_ftor, _list.begin(), _list.end()));
//_threads.create_thread(bind(_ftor, _list.begin(), _list.end()));
_threads.join_all();
}
private:
boost::thread_group _threads;
ftor _ftor;
vector<data> _list;
};
template<typename data>
class Ftor
{
public:
//template <class Args> struct sig { typedef void type; }
explicit Ftor () {}
void operator() (typename vector<data>::iterator &startItr, typename vector<data>::iterator &endItr)
{
for_each(startItr, endItr, cout << bl::_1 << constant("."));
}
}
I also tried typedef-ing 'type' as I thought my problem might have something to do with the Sig Template as the functor itself is templated.
The error I am getting is:
error: no matching function for call to ‘boost::lambda::function_adaptor<Ftor<int> Foo<Ftor<int>, int>::*>::apply(Ftor<int> Foo<Ftor<int>, int>::* const&, const __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int>> >&, const __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >&)’
with a bunch of preamble beforehand.
Thanks in advance for any help!
Okay I've modified the code taking in Eric's suggestions below resulting in the following code:
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/thread.hpp>
#include <vector>
using namespace boost::lambda;
using namespace std;
namespace bl = boost::lambda;
template<typename ftor, typename data>
class Foo
{
public:
explicit Foo()
{
}
void doFtor ()
{
_threads.create_thread(bl::bind(boost::ref(_ftor), _list.begin(), _list.end()));
_threads.join_all();
}
private:
boost::thread_group _threads;
ftor _ftor;
vector<data> _list;
};
template<typename data>
class Ftor
{
public:
typedef void result_type;
explicit Ftor () {}
result_type operator() (typename vector<data>::iterator &startItr, typename vector<data>::iterator &endItr)
{
for_each(startItr, endItr, cout << bl::_1 << constant("."));
return ;
}
};
However this results in another compile error:
/usr/local/include/boost/lambda/detail/function_adaptors.hpp:45: error: no match for call to ‘(Ftor<int>) (const __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >&, const __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >&)’
ftor.h:41: note: candidates are: void Ftor<data>::operator()(typename std::vector<data, std::allocator<_CharT> >::iterator&, typename std::vector<data, std::allocator<_CharT> >::iterator&) [with data = int]
/usr/local/include/boost/lambda/detail/function_adaptors.hpp:45: error: return-statement with a value, in function returning 'void'
It seems having defined void as a result_type it is now expecting the operator() to return something. I tried returning result_type from within the function but this also generated errors. Any ideas?
Sig (or in your case, simply typedef void result_type; is necessary.
IIRC, lambda::bind makes const copies of its arguments.
There is thus a problem with functors with non-const operator(). This is solved by making Ftor::operator()const or by wrapping (in doFtor()), _ftor with boost::ref
There is a similar problem with the iterators. Wrapping in boost::ref here won't work directly because it would end up using a reference to a temporary. The simpler solution is to modify Ftor::operator() to take its arguments by copy.
The simplest is thus to modify Ftor so that its operator() is const and it takes its arguments by copy:
void operator() (typename vector<data>::iterator startItr, typename vector<data>::iterator endItr)const
If you really can't make Ftor::operator() const, you could modify doFtor() as follows (but it is still necessary to make Ftor::operator() take its arguments by copy):
_threads.create_thread(bind(boost::ref(_ftor), _list.begin(), _list.end()));