vendredi 11 août 2023

Type trait that checks whether function parameter can be initialized by a certain type

I want to refactor a function interface that uses Boost_PP by using variadic templates. The interface allows to register function objects. In pseudo code this looks like this:


template<typename F, 
         typename ReturnValue, typname Arg1, ..., typename ArgN,
         typename Example1, ..., typename ExampleN>
void registerFunction(F&& function, 
                      std::string parameterName1, std::string parameterDescription1, Example1 e1, ...., 
                      std::string parameterNameN, std::string parameterDescriptionN, ExampleN en);

float rectangleArea(const float width, const float heigth);

registerFunction<float, const float, const float>(&rectangleArea, 
                                "width", "width of rectangle", 2,
                                "height", "height of parameter", 4);


I tried to replace the Boost_PP created function with:


template<typename F, typename ReturnValue, typname ... Args, typename ... Description>
void registerFunction(F&& function, Description&& ... desc);

However, every third element must be so that if forwarded to the function the object is callable. Eg. in pseudo code:

function(std::forward<Description2>(desc2),std::forward<Description5>(desc5),.....)

I tried to use SFINAE to restrict my function to valid descriptions.


template<typename Args, typename Descriptions, typename Enable = void>
struct ParameterParser: std::false_type
{};

// recursion end
template<typename Enable>
struct ParameterParser<std::tuple<>, std::tuple<>, Enable>: std::true_type
{};

template<typename Argument, typename ... RemainingArguments, typename Name, typename ParameterDescription, typename Example, typename ... RemainingDescription>
struct ParameterParser<std::tuple<Argument, RemainingArguments...>, 
                       std::tuple<Name, ParameterDescription, Example, RemainingDescription...>, 
                       typename std::enable_if<
                           (sizeof...(RemainingArguments)) * 3 == sizeof...(RemainingDescription)
                            && std::is_convertible<Example, Argument>::value
                            && std::is_convertible<ParameterDescription, std::string>::value
                            && std::is_convertible<Name, std::string>::value>::type> 
      :ParameterParser<std::tuple<RemainingArguments...>, std::tuple<RemainingDescription...>>
{};

template<typename ArgumentTuple, typename DescriptionTuple>
using ValidDescription = typename std::enable_if<ParameterParser<ArgumentTuple, DescriptionTuple>::value>::type;

template<typename ReturnValue, typename ... Args, typename ... Description, typename F,
         typename = ValidDescription<std::tuple<Args...>, std::tuple<Description...>>>
void registerFunction(F&& function, Description&& ... desc)
{

}

However, it seems like std::is_convertible<Example, Argument>::value is not the correct trait for my use case. The problem is the following:

void functionToRegister(int&);

// this won't compile
registerFunction<void, int&>(&functionToRegister, "a parameter", "a description", 1);

The 1 is deduced as int and std::is_convertible<int, int&>::value is false.

How can i express a condition between two types 'ARG' and 'CallOBJ' that says: the parameter a of a function f(ARG a) can be initialized with something of type CallOBJ.

Note that i am limited to C++11 :(

Here is a godbold link to my problem: https://godbolt.org/z/PMGon1f8q

Aucun commentaire:

Enregistrer un commentaire