Consider the following template recursion function. Its purpose is to create a vector containing all values in the enum class EnumClassName for which its associated predicate function bool xxx<N>() returns true.
template <EnumClassName N = EnumClassName::LAST_ENUM_VALUE>
std::vector<EnumClassName> xxx_stuff()
{
std::vector<EnumClassName> v = xxx_stuff<EnumClassName(static_cast<int>(N) - 1)>();
if (xxx<N>()) {
v.emplace_back(N);
}
return v;
}
with the recursion base case:
template <> std::vector<EnumClassName> xxx_stuff<EnumClassName::FIRST_ENUM_VALUE>()
{
return {};
}
Now assume there are 10 of these functions where the only thing that differs is the xxx name. I.e. we have the functions alpha_stuff with a corresponding predicate function alpha<N>, beta_stuff with a corresponding predicate function beta<N>, etc etc.
Is there any way to not duplicate the above function 10 times, where the only difference is the replacement of xxx with alpha, beta etc in each duplicate?
I am not able to just loop over the enum values, because the xxx<N> functions in turn call another function yyy<N> which will not accept a runtime-generated template argument <N> (forgive my lingo there, I actually have no idea what I'm talking about or how this works, I just know it gave me compilation errors when I tried it that way).
You cannot pass function template as argument or overloaded function, but you can wrap the call in a type, and then
template <typename EnumClassName,
EnumClassName N = EnumClassName::LAST_ENUM_VALUE,
typename F>
std::vector<EnumClassName> xxx_stuff(F&& f)
{
std::vector<EnumClassName> v =
xxx_stuff<EnumClassName, EnumClassName(static_cast<int>(N) - 1)>(f);
if (f(std::integral_constant<EnumClassName, N>{})) {
v.emplace_back(N);
}
return v;
}
with usage
auto v = xxx_stuff<E>([](auto n) { return xxx<n()>(); });
Demo
Note: That recursion can be avoided using std::integer_sequence.
Related
I have a class Frobnicator that handles various requests.
class Frobnicator
{
public:
// Handlers are member functions.
// They optionally take some input. They optionally return some output. But they always take the context!
// There are more types than just int involved, but it's always just one input or void, and one output or void.
void performSomething(Context* context) { /* ... */ } // Takes void, returns void
void setSomething (Context* context, int input) { /* ... */ } // Takes int , returns void
int getSomething (Context* context) { /* ... */ } // Takes void, returns int
int convertSomething(Context* context, int input) { /* ... */ } // Takes int , returns int
template<typename TResult, typename TParameter>
void registerHandler(std::string identifier, TResult(Frobnicator::* handler)(Context*, TParameter))
{
// The external API actually wants a callback that takes and returns JSON. We give it a lambda that does the conversion and calls the actual member function.
// The identifier tells the external API which callback to call for which request. It's not relevant for this question, just to show the idea. Think of something like a REST API.
someExternalApiThatWantsJson.registerHandler(identifier, [&](Context* context, Json input)
{
// Idealy, this would be a one-liner.
//return Json::convertFrom((this->*handler)(context, input.convertTo<TParameter>()));
// But calling Json.convertTo<void>() and Json::convertFrom(void) does not work automagically anyways, so we need to split it up manually:
Json result;
if constexpr (std::is_same<TResult, void>::value)
if constexpr (std::is_same<TParameter, void>::value) (this->*handler)(context ) ; // Takes void, returns void
else (this->*handler)(context, input.convertTo<TParameter>()) ; // Takes something, returns void
else
if constexpr (std::is_same<TParameter, void>::value) result = Json::convertFrom((this->*handler)(context )); // Takes void, returns something
else result = Json::convertFrom((this->*handler)(context, input.convertTo<TParameter>())); // Takes something, returns something
return result;
});
}
// Set up the handlers.
void setup()
{
// The problem is that some of these calls don't work:
registerHandler ("PerformSomething", &Frobnicator::performSomething); // "failed template argument deduction"
registerHandler<void, void>("PerformSomething", &Frobnicator::performSomething); // Trying to specify the types explicitly: "substitution failure [with TResult = void, TParameter = void]: argument may not have 'void' type"
registerHandler ("SetSomething" , &Frobnicator::setSomething); // Compiles fine
registerHandler ("GetSomething" , &Frobnicator::getSomething); // "failed template argument deduction"
registerHandler<int , void>("GetSomething" , &Frobnicator::getSomething); // Trying to specify the types explicitly: "substitution failure [with TResult = int, TParameter = void]: argument may not have 'void' type"
registerHandler ("ConvertSomething", &Frobnicator::convertSomething); // Compiles fine
}
};
TResult can be int or void and it works fine. But it only works when TParameter isn't void.
How can I make registerHandler also accept pointers to functions that take no arguments?
The idea is to have the member functions' signatures very clean and the calls to registerHandler mostly clean. So giving performSomething and getSomething a dummy parameter is out of the question. Manually specifying the types when calling registerHandler is ugly but I'll accept it, if it's necessary.
The body of registerHandler is relatively short and mostly deals with distinguishing void from non-void anyways, so providing a specialization for when TParameter is void would be a fine solution:
template<typename TResult>
void registerHandler<TResult, void>(std::string identifier, TResult(Frobnicator::* handler)(Context*))
Except that "function template partial specialization is not allowed".
Use template parameter pack to deal with void / non-void cases, since the number of arguments is indetermined (1 or 2).
template<typename TResult, typename... TParameter>
void registerHandler(std::string identifier, TResult(Frobnicator::* handler)(Context*, TParameter...))
The second parameter accepts a pointer to member function whose the first argument is required to be Context*, that's it.
Then,
void setup()
{
registerHandler ("PerformSomething", &Frobnicator::performSomething);
registerHandler ("SetSomething" , &Frobnicator::setSomething);
registerHandler ("GetSomething" , &Frobnicator::getSomething);
registerHandler ("ConvertSomething", &Frobnicator::convertSomething);
}
The implementation of registerHandler may also need some changes, the std::is_same_v<TParameter, void> can be replaced by sizeof...(TParameter) == 0.
Demo
Well. Sometimes you waste an hour because you narrowly miss a solution. Just don't make it a specification:
template<typename TResult>
void registerHandler(std::string identifier, TResult(Frobnicator::* handler)(Context*))
But!: This is a valid solution for me, but it would be nice to have a solution that does not require duplicating almost the entire function body. So better answers are absolutely welcome!
What I would do is follow the 0 1 infinity rule and write infinity instead of 2 cases.
Support any number of arguments. Map to tuples. Empty tuples for 0, mono for 1, etc.
Then the conversion code should work naturally. You can bind the call to be (take tuple, return tuple), then write the input/output code to handle 0, 1, or n from/to json.
Now the json function call logic no longer cares about void. The packing unpacking does. And the code that calls the raw function from tuples and packs it into a tuple return does.
template<class F>
auto packresult(F&&f){
if constexpr(f() is void)
f()
return tuple<>{};
else if constexpr(f returns a tuple)
return f();
else
return std::make_tuple(f());
}
Now you do the same for inputs
auto unpackargs(auto&&f){
return [f](auto&&tup){
return std::apply(f, tup);
}
}
which makes your code look like:
converttupletojson(packresult(bind(unpackargs(bind this to method), getargsfromjson<Args...>())))
and viola, the most surprising musical instrument.
I have some very heavily templated code, where I need to pass configuration flags encoded as unsigned via a template.
template<unsigned T>
struct MemoryTraits
{
// Do something with T...
};
There are some predefined flags, that I'm supposed to bit-or, and pass to the struct above, then it will be passed further to other templated functions and classes.
SomeTemplatedContainer</*other parameters */, MemoryTraits<Trait1 | Trait2>> myContainer{};
I was trying to create a variadic template function, that would take any number of flags, and alias the MemoryTraits, depending on template parameters.
template<unsigned ...Traits>
void foo() {
using MyMemoryTraits = MemoryTraits<(Traits | ...)>;
SomeTemplatedContainer</*other parameters */, MyMemoryTraits> myContainer{}
}
I tried to implement it using c++17 fold expressions, and everything works when I invoke foo with one or more arguments, but when calling it without any, I get the following compile time error.
error: expected expression
foo(); -> fold expression has empty expansion
From what I understand, it's because an empty fold expression with operator | doesn't have a default value, is there any way I can provide a default value to it? So far I came up with the following helper lambda.
constexpr auto TraitsHelper = [](){
if constexpr (sizeof...(Traits) == 0){
return 0;
} else if constexpr(sizeof...(Traits) == 1){
constexpr std::array TRAITS{Traits...};
return TRAITS[0];
} else {
return (Traits | ...);
}
};
It works, at least for now, but is there some better way to fix this issue?
While building a small lambda-based metaprogramming library, I had the necessity of using recursion in a C++14 generic lambda, to implement a left-fold.
My own solution was passing the lambda itself as one of its parameters, like this:
template <typename TAcc, typename TF, typename... Ts>
constexpr auto fold_l_impl(TAcc acc, TF f, Ts... xs)
{
// Folding step.
auto step([=](auto self)
{
return [=](auto y_acc, auto y_x, auto... y_xs)
{
// Compute next folding step.
auto next(f(y_acc, y_x));
// Recurse if required.
return static_if(not_empty(y_xs...))
.then([=]
{
// Recursive case.
return self(self)(next, y_xs...);
})
.else_([=]
{
// Base case.
return next;
})();
};
});
// Start the left-fold.
return step(step)(acc, xs...);
}
step is the "main" lambda that starts off the recursion. It returns a function with the desired left-fold signature (accumulator, current item, remaining items...).
The function calls itself recursively by using self(self)(next, y_xs...).
I've recently come across this proposal that wants to add a Y Combinator to the Standard Library, and after reading it, it seems extremely similar to what I am doing here.
Unfortunately, the concept of the Y Combinator still doesn't "click" for me - I am missing something and I cannot visualize how to generalize what I did with the self parameter for any function, avoiding the step boilerplate.
I've read this excellent StackOverflow answer regarding the matter, but it still didn't "click" for me.
(From that answer) a recursive factorial is defined this way:
fact =
(recurs) =>
(x) =>
x == 0 ? 1 : x * recurs(x - 1);
The recurs parameter seems to have the same role as my self parameter. What I do not understand is how recurs is called without passing recurs into itself again.
I have to call self like this: self(self)(params...).
recurs, however, is called like recurs(params...).
Attempting to call self(params...) results in a compiler error informing me that self requires only a single parameter (which is the auto self lambda parameter).
What am I missing here? How could I rewrite my fold_l_impl lambda in such a way that its recursion could be generalized through the use of a Y Combinator?
Here is a y combinate where the lambda is passed a recurs that doesn't need to be passed recurs:
template<class F>
struct y_combinate_t {
F f;
template<class...Args>
decltype(auto) operator()(Args&&...args)const {
return f(*this, std::forward<Args>(args)...);
}
};
template<class F>
y_combinate_t<std::decay_t<F>> y_combinate( F&& f ) {
return {std::forward<F>(f)};
};
then you do:
return y_combinate(step)(acc, xs...);
and change
return self(self)(next, y_xs...);
to
return self(next, y_xs...);
the trick here is I used a non-lambda function object that has access to its own this, which I pass to f as its first parameter.
I'm trying to send D's sort function as a template argument to the pipe function. When I use sort without template arguments it works:
import std.stdio,std.algorithm,std.functional;
void main()
{
auto arr=pipe!(sort)([1,3,2]);
writeln(arr);
}
However, when I try to use sort with a template argument:
import std.stdio,std.algorithm,std.functional;
void main()
{
auto arr=pipe!(sort!"b<a")([1,3,2]);
writeln(arr);
}
I get an error - main.d(5): Error: template instance sort!("b<a") sort!("b<a") does not match template declaration sort(alias less = "a < b",SwapStrategy ss = SwapStrategy.unstable,Range)
Why does it happen? sort!"b<a" works on it's own, and it has the same arguments and return types as sort, so why does pipe accept sort but not sort!"b<a"? And is there a correct syntax for what I try to do?
UPDATE
OK, I've tried to wrap the sort function. The following code works:
import std.stdio,std.algorithm,std.functional,std.array;
template mysort(string comparer)
{
auto mysort(T)(T source)
{
sort!comparer(source);
return source;
}
}
void main()
{
auto arr=pipe!(mysort!"b<a")([1,3,2]);
writeln(arr);
}
So why doesn't the original version work? is this because of the extra template parameters sort takes?
Yes it's because of the extra template parameters — specifically the Range parameter. The problem can be reduced to
size_t sort2(alias f, Range)(Range range)
{
return 0;
}
alias sort2!"b<a" u;
The instantiation sort!"b<a" will fail because the Range is not determined. The function call sort2!"b<a"([1,2,3]) works because the parameter [1,2,3] can tell the compiler the type Range is int[]. This is known as "implicit function template instantiation (IFTI)". But IFTI only works when it is used as a function. In your use case, sort!"b<a" is instantiated without providing all parameters, thus the error.
This can be fixed by making the input a function literal, which is just similar to your mysort solution:
auto arr = pipe!(x => sort!"b<a"(x))([1,3,2]);
Or you could provide all required template parameters. This makes the code very unreadable though.
auto arr = pipe!(sort!("b<a", SwapStrategy.unstable, int[]))([1,3,2]);
I have one template function which will take a pointer type and i have instantiated it before calling.
i have written function with its dummy implementation as follows:
template<T>fun_name( const T *p )
{
//written functionality which will give me class name that i will store into string Variable
e.g. i got output like this string Var = "First_class" or string Var = "Second_class"
//Using this class name i will call one function of that class
if(Var == "Fisrt_class")
{
First_class::static_function_name(p);
}
if(Var == "Second_class")
{
Second_class::static_function_name(p);
}
}
and in global scope i instantiated this function for two variables as like below:
template<first_class>static_function_name(const First_class *)
template<Second_class>static_function_name(const Second_class *)
above code gives me error that
error: no matching function call in Second_class::static_function_class(const Fisrt_class*)
error: no matching function call in First_class::static_function_class(const Second_class*)
thanks in advance!
I think this :
template<typename T> // template<class T> is equally valid!
void fun_name( const T *p )
{
T::static_function_name(p);
}
is enough!
Two more errors is fixed in the above code:
Mention the keyword typename in template<T> in your code. You can also write template<class T> which is equally valid.
Mention the return type of the function template as well.
Your function template "calls" each of the static functions in each class. Even though program flow may never get to one of the calls, the compiler still has to figure out the code for each of them.
So when you instantiate:
template<first_class>fun_name(const first_class*)
the compiler tries to compile the entire function with T = first_class, which means at some point inside the function, it will try to compile the function call:
Second_class::static_function_name(p);
But since variable p is a pointer to first_class, the compiler doesn't find the function.
If you want conditional compilation, try specializing your function instead so the compiler only compiles the function call you intended for each type:
template <T> fun_name (const T* p);
template <> fun_name<first_class>(const first_class* p) {
first_class::static_function_name(p);
}
template <> fun_name<second_class>(const second_class* p) {
second_class::static_function_name(p);
}
Alternatively, you can use member functions which seem to be intended for what you are trying to do here. Then you can create objects and call the functions directly:
first_class f;
second_class s;
f.function();
s.function();
try changing to ,
template<typename T>
void fun_name( const T *p )
{
T::static_function_name(p);
}