Ambiguity errors with variadic templates and mixins - c++

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

Use a function pointer as template function type parameter?

#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>
);

Duplicate type generated from template

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;

constexpr: saving calculation results [duplicate]

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.

Compilation error with Type Traits in boost::type_traits::conditional

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;

C++ templated functor in lambda expression

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()));