I would like to improve the code so that it is convenient to interact with it.
struct prototype {
template <class... T1>
prototype(T1&&... args) {
auto p = std::tie(args...);
std::cout << std::get<0>(p) << std::endl;
if constexpr(std::tuple_size_v<decltype(p)> >= 3) {
std::cout << std::get<2>(p) << std::endl;
}
}
};
int option_1 = 10;
std::string option_2 = "test2";
auto option_3 = 0.41;
std::vector<int> option_4(10);
int main() {
prototype p1(option_1, option_2, option_3, option_4);
prototype p2(option_1, option_2, option_3);
prototype p3(option_1, option_2);
prototype p4(option_1);
}
i would like to do so
std::cout << option_1 << std::endl;
if constexpr (std::tuple_size_v<decltype(p)> >= 3) {
std::cout << option_2 << std::endl;
}
I don't like this option std::get<0>(p)
Any ideas how to replace the call to tuple?
You can also see the option on https://godbolt.org/z/bT4Wzjco8
You can create a variable template out of a lambda. At the end of the day all you want is a compile time constant to pass to std::get:
template <std::size_t N>
constexpr auto option = [] (auto p) -> auto&& { return std::get<N-1>(p); };
This can be used as option<1>(p)
Demo
The familiar template syntax for lambdas may seem as another alternative:
constexpr auto option = []<std::size_t N>(auto p) { return std::get<N-1>(p); };
Here the argument to std::get is passed as a non type template parameter. As #Davis Herring mentions, this unfortunately does not mean the lambda is then to be used as option<1>(p). The reason being that the lambda is not itself a template, its function call operator is. The proposal changes nothing on the templateness of the lambda itself. As a result the lambda above is invocable as
option.operator()<1>(p)
Demo
Given the following setup:
// ***** Library Code *****
#include <concepts>
template <std::invocable CbT>
struct delegated {
explicit constexpr delegated(CbT cb) : cb_(std::move(cb)) {}
private:
[[no_unique_address]] CbT cb_;
};
// ***** User Code *****
#include <iostream>
namespace {
inline constexpr void func() {}
}
struct MyFunc {
constexpr void operator()() const {}
};
int main() {
void (*func_ptr)() = func;
auto from_func = delegated{func};
auto from_func_ptr = delegated{func_ptr};
auto from_lambda = delegated{[](){}};
auto from_functor = delegated{MyFunc{}};
std::cout << "func: " << sizeof(from_func) << "\n";
std::cout << "func_ptr: " << sizeof(from_func_ptr) << "\n";
std::cout << "lambda: " << sizeof(from_lambda) << "\n";
std::cout << "functor: " << sizeof(from_functor) << "\n";
}
It produces, on GCC-x86-64 (See on godbolt):
func: 8 <----- Unfortunate
func_ptr: 8 <----- Fair enough
lambda: 1 <----- Neat
functor: 1 <----- Neat
None of this is particularly surprising.
However, it's frustrating that an undecayed lambda is preferable to using a function. And adding a note that delegated{[]{func();}} reduces the storage overhead is not exactly user-friendly, and makes for a very poor library interface.
Is there a way to do away with the storage overhead in the func case while maintaining a consistent user-facing API?
My current suspicion is that this is not possible without resorting to macros, on account of func not having, or decaying into, any type that would distinguish it from other functions with the same signature. I'm hoping that I overlooked something.
N.B. I get that something along the lines of delegated<func>() is a possibility, but unless I can prevent delegated{func} while still allowing delegated{func_ptr}, then that would be practically pointless.
Edit: To clarify the context a little bit: I am writing delegated in a library, and I don't want users of said library to have to worry about this. Or at least have the process be compiler-assisted instead of being documentation-dependant.
There are no objects of function types. The type will be adjusted to be a function pointer, which is why you delegated{func} and delegated{func_ptr} are exactly the same thing and former cannot be smaller.
Wrap the function call inside a function object (lambda, if you so prefer) to avoid the overhead of the function pointer.
If you would like to prevent the accidental use of the adjusted/decayed function pointer case when user tries to pass a function, then you could use a deleted overload for function references. I don't know how that could be achieved with CTAD, but if you provide a function interface, it could be done like this:
constexpr auto
make_delegated(std::invocable auto CbT)
{
return delegated{std::move(CbT)};
}
template<class... Args>
constexpr auto
make_delegated(auto (&cb)(Args...)) = delete;
Edit: Combining ideas with Human-Compiler's answer
template <auto CbT>
constexpr auto
make_delegated_fun() {
return delegated{ []{ CbT(); } };
}
constexpr auto
make_delegated(std::invocable auto CbT)
{
return delegated{std::move(CbT)};
}
template<class... Args>
constexpr auto
make_delegated(auto (&cb)(Args...)) {
// condition has to depend on template argument;
// just false would cause the assert to trigger without overload being called.
static_assert(!std::is_reference_v<decltype(cb)>, "please use make_delegated_fun");
};
auto from_func1 = make_delegated(func); // fails to compile
auto from_func2 = make_delegated_fun<func>(); // OK
auto from_func_ptr = make_delegated(func_ptr); // OK, pointer overhead
auto from_lambda = make_delegated([](){}); // OK
auto from_functor = make_delegated(MyFunc{}); // OK
Caveat, this would prevent following, and the example wouldn't work using make_delegated_fun either so the message would be misleading. The example could easily be rewritten to use function pointers or capturing lambda though:
auto& fun_ref = condition ? fun1 : fun2;
make_delegated(fun_ref); // fails to compile, suggests make_delegated_fun
make_delegated_fun<fun_ref>(); // fails to compile, not constexpr
make_delegated(&fun_ref); // OK, pointer overhead
The only way to really remove the "storage" from a function like this is to use the value at compile-time. The only real way to accomplish this is through a non-type template argument.
A factory function could do this easily with little changes, and keeps the implementation simple. You just need to accept the callable object as a template non-type argument -- such as an auto parameter, so that its known at compile-time without any storage requirements.
One way to perform this is to just use your lambda-wrapping solution with the existing code:
template <auto Fn>
auto make_delegated() {
return delegated{ []{ Fn(); } };
}
Then the following works:
auto from_func = make_delegated<&func>();
std::cout << "func: " << sizeof(from_func) << "\n";
this yields the correct value:
func: 1
Live Example
As an alternative measure, you could also require the user to wrap the function itself in a sentinel type that carries the data:
template <auto Fn>
struct empty_func{
auto operator()() { return Fn(); }
};
This would be almost equivalent to using a lambda, though the user instead only has to do:
auto from_func = delegated{empty_func<&func>{}};
The point is just that the function needs to be carried at compile-time somewhere.
Passing the function pointer as a template argument does not require any space at runtime. For example:
template <auto F>
struct delegated_erased {
template <typename... argument_t>
auto operator()(argument_t&&... argument){
F(std::forward<argument_t>(argument)...);
}
};
auto from_func = delegated_erased<func>{};
std::cout << "func: " << sizeof(from_func) << "\n"; // 1
With a helper function, you can combine this with your code:
template <typename result, typename ... argument>
delegated<result> make_delegated(result(&)(argument...)) = delete;
template <typename T>
delegated<T> make_delegated(T f) {
return delegated{std::move(f)};
};
template <auto F>
delegated_erased<F> make_delegated(){
return {};
}
Which allows you to do:
auto from_func = make_delegated<func>();
//auto from_func = make_delegated(func); // error: call to deleted function 'make_delegated'
auto from_func_ptr = make_delegated(func_ptr);
auto from_lambda = make_delegated([](){});
auto from_functor = make_delegated(MyFunc{});
I've written a template code that takes a functor as an argument and after some processing, executes it. Although someone else might pass that function a lambda, a function pointer or even an std::function but it is meant primarily for lambda(not that I ban other formats). I want to ask how should I take that lambda - by value? by reference? or something else.
Example code -
#include <iostream>
#include <functional>
using namespace std;
template<typename Functor>
void f(Functor functor)
{
functor();
}
void g()
{
cout << "Calling from Function\n";
}
int main()
{
int n = 5;
f([](){cout << "Calling from Temp Lambda\n";});
f([&](){cout << "Calling from Capturing Temp Lambda\n"; ++n;});
auto l = [](){cout << "Calling from stored Lambda\n";};
f(l);
std::function<void()> funcSTD = []() { cout << "Calling from std::Function\n"; };
f(funcSTD);
f(g);
}
In above code, I've a choice of making it either of these -
template<typename Functor>
void f(Functor functor)
template<typename Functor>
void f(Functor &functor)
template<typename Functor>
void f(Functor &&functor)
What would be the better way and why? Are there any limitations to any of these?
As a possible drawback, note that passing by copy could not work if the lambda isn't copyable. If you can get away with it, passing by copy is just fine.
As an example:
#include<memory>
#include<utility>
template<typename F>
void g(F &&f) {
std::forward<F>(f)();
}
template<typename F>
void h(F f) {
f();
}
int main() {
auto lambda = [foo=std::make_unique<int>()](){};
g(lambda);
//h(lambda);
}
In the snippet above, lambda isn't copyable because of foo. Its copy constructor is deleted as a consequence of the fact that the copy constructor of a std::unique_ptr is deleted.
On the other side, F &&f accepts both lvalue and rvalue references being it a forwarding reference, as well as const references.
In other terms, if you want to reuse the same lambda as an argument more than once, you cannot if your functions get your object by copy and you must move it for it's not copyable (well, actually you can, it's a matter of wrapping it in a lambda that captures the outer one by reference).
As lambda expressions can have their own fields (like classes), copying / using reference can cause different results. Here's simple example:
template<class func_t>
size_t call_copy(func_t func) {
return func(1);
}
template<class func_t>
size_t call_ref(func_t& func) {
return func(1);
}
int main() {
auto lambda = [a = size_t{0u}] (int val) mutable {
return (a += val);
};
lambda(5);
// call_ref(lambda); – uncomment to change result from 5 to 6
call_copy(lambda);
std::cout << lambda(0) << std::endl;
return 0;
}
Btw if you want to judge it by performance there's actually no difference, lambdas are very small and easy to copy.
Also – if you want to pass lambda as parameter (not a variable that contains it) you should use forwarding reference so it could work.
Basically I want to do this:
Can I use a lambda function or std::function object in place of a function pointer?
clearly that is impossible for now for functions that expect a function pointer. However, it will work for a function that expects a functor ( I have done it before with stl's sort() function)
However, I don't know how to write a function that takes a functor as an argument!
Anyone?
I don't know how to write a function that takes a functor as an argument!
Since nobody has posted the std::function solution yet:
#include <functional>
#include <iostream>
void foo(std::function<int(int)> f)
{
for (int i = 0; i < 10; ++i)
{
std::cout << f(i) << ' ';
}
std::cout << '\n';
}
int main()
{
foo( [](int x) { return x*x; } );
}
You can use boost::function in pre C++11 compilers.
Just write the function to take an arbitrary type:
template <typename Func>
void foo(Func fun)
{
fun(...);
}
This will then work with function pointers, functors and lambdas all the same.
void f() {}
struct G
{
void operator()() {}
};
foo(&f); // function pointer
foo(G()); // functor
foo([]() { ... }); // lambda
It has to be a template, like:
template<class F>
void foo( const F& f )
{
f(x,y);
}
I'm looking for the rules involving passing C++ templates functions as arguments.
This is supported by C++ as shown by an example here:
void add1(int &v) { v += 1 }
void add2(int &v) { v += 2 }
template <void (*T)(int &)>
void doOperation()
{
int temp = 0;
T(temp);
std::cout << "Result is " << temp << std::endl;
}
int main()
{
doOperation<add1>();
doOperation<add2>();
}
Learning about this technique is difficult, however. Googling for "function as a template argument" doesn't lead to much. And the classic C++ Templates The Complete Guide surprisingly also doesn't discuss it (at least not from my search).
The questions I have are whether this is valid C++ (or just some widely supported extension).
Also, is there a way to allow a functor with the same signature to be used interchangeably with explicit functions during this kind of template invocation?
The following does not work in the above program, at least in Visual C++, because the syntax is obviously wrong. It'd be nice to be able to switch out a function for a functor and vice versa, similar to the way you can pass a function pointer or functor to the std::sort algorithm if you want to define a custom comparison operation.
struct add3 {
void operator() (int &v) {v += 3;}
};
...
doOperation<add3>();
Pointers to a web link or two, or a page in the C++ Templates book would be appreciated!
Yes, it is valid.
As for making it work with functors as well, the usual solution is something like this instead:
template <typename F>
void doOperation(F f)
{
int temp = 0;
f(temp);
std::cout << "Result is " << temp << std::endl;
}
which can now be called as either:
doOperation(add2);
doOperation(add3());
See it live
The problem with this is that if it makes it tricky for the compiler to inline the call to add2, since all the compiler knows is that a function pointer type void (*)(int &) is being passed to doOperation. (But add3, being a functor, can be inlined easily. Here, the compiler knows that an object of type add3 is passed to the function, which means that the function to call is add3::operator(), and not just some unknown function pointer.)
Template parameters can be either parameterized by type (typename T) or by value (int X).
The "traditional" C++ way of templating a piece of code is to use a functor - that is, the code is in an object, and the object thus gives the code unique type.
When working with traditional functions, this technique doesn't work well, because a change in type doesn't indicate a specific function - rather it specifies only the signature of many possible functions. So:
template<typename OP>
int do_op(int a, int b, OP op)
{
return op(a,b);
}
int add(int a, int b) { return a + b; }
...
int c = do_op(4,5,add);
Isn't equivalent to the functor case. In this example, do_op is instantiated for all function pointers whose signature is int X (int, int). The compiler would have to be pretty aggressive to fully inline this case. (I wouldn't rule it out though, as compiler optimization has gotten pretty advanced.)
One way to tell that this code doesn't quite do what we want is:
int (* func_ptr)(int, int) = add;
int c = do_op(4,5,func_ptr);
is still legal, and clearly this is not getting inlined. To get full inlining, we need to template by value, so the function is fully available in the template.
typedef int(*binary_int_op)(int, int); // signature for all valid template params
template<binary_int_op op>
int do_op(int a, int b)
{
return op(a,b);
}
int add(int a, int b) { return a + b; }
...
int c = do_op<add>(4,5);
In this case, each instantiated version of do_op is instantiated with a specific function already available. Thus we expect the code for do_op to look a lot like "return a + b". (Lisp programmers, stop your smirking!)
We can also confirm that this is closer to what we want because this:
int (* func_ptr)(int,int) = add;
int c = do_op<func_ptr>(4,5);
will fail to compile. GCC says: "error: 'func_ptr' cannot appear in a constant-expression. In other words, I can't fully expand do_op because you haven't given me enough info at compiler time to know what our op is.
So if the second example is really fully inlining our op, and the first is not, what good is the template? What is it doing? The answer is: type coercion. This riff on the first example will work:
template<typename OP>
int do_op(int a, int b, OP op) { return op(a,b); }
float fadd(float a, float b) { return a+b; }
...
int c = do_op(4,5,fadd);
That example will work! (I am not suggesting it is good C++ but...) What has happened is do_op has been templated around the signatures of the various functions, and each separate instantiation will write different type coercion code. So the instantiated code for do_op with fadd looks something like:
convert a and b from int to float.
call the function ptr op with float a and float b.
convert the result back to int and return it.
By comparison, our by-value case requires an exact match on the function arguments.
Function pointers can be passed as template parameters, and this is part of standard C++
. However in the template they are declared and used as functions rather than pointer-to-function. At template instantiation one passes the address of the function rather than just the name.
For example:
int i;
void add1(int& i) { i += 1; }
template<void op(int&)>
void do_op_fn_ptr_tpl(int& i) { op(i); }
i = 0;
do_op_fn_ptr_tpl<&add1>(i);
If you want to pass a functor type as a template argument:
struct add2_t {
void operator()(int& i) { i += 2; }
};
template<typename op>
void do_op_fntr_tpl(int& i) {
op o;
o(i);
}
i = 0;
do_op_fntr_tpl<add2_t>(i);
Several answers pass a functor instance as an argument:
template<typename op>
void do_op_fntr_arg(int& i, op o) { o(i); }
i = 0;
add2_t add2;
// This has the advantage of looking identical whether
// you pass a functor or a free function:
do_op_fntr_arg(i, add1);
do_op_fntr_arg(i, add2);
The closest you can get to this uniform appearance with a template argument is to define do_op twice- once with a non-type parameter and once with a type parameter.
// non-type (function pointer) template parameter
template<void op(int&)>
void do_op(int& i) { op(i); }
// type (functor class) template parameter
template<typename op>
void do_op(int& i) {
op o;
o(i);
}
i = 0;
do_op<&add1>(i); // still need address-of operator in the function pointer case.
do_op<add2_t>(i);
Honestly, I really expected this not to compile, but it worked for me with gcc-4.8 and Visual Studio 2013.
In your template
template <void (*T)(int &)>
void doOperation()
The parameter T is a non-type template parameter. This means that the behaviour of the template function changes with the value of the parameter (which must be fixed at compile time, which function pointer constants are).
If you want somthing that works with both function objects and function parameters you need a typed template. When you do this, though, you also need to provide an object instance (either function object instance or a function pointer) to the function at run time.
template <class T>
void doOperation(T t)
{
int temp=0;
t(temp);
std::cout << "Result is " << temp << std::endl;
}
There are some minor performance considerations. This new version may be less efficient with function pointer arguments as the particular function pointer is only derefenced and called at run time whereas your function pointer template can be optimized (possibly the function call inlined) based on the particular function pointer used. Function objects can often be very efficiently expanded with the typed template, though as the particular operator() is completely determined by the type of the function object.
The reason your functor example does not work is that you need an instance to invoke the operator().
Came here with the additional requirement, that also parameter/return types should vary.
Following Ben Supnik this would be for some type T
typedef T(*binary_T_op)(T, T);
instead of
typedef int(*binary_int_op)(int, int);
The solution here is to put the function type definition and the function template into a surrounding struct template.
template <typename T> struct BinOp
{
typedef T(*binary_T_op )(T, T); // signature for all valid template params
template<binary_T_op op>
T do_op(T a, T b)
{
return op(a,b);
}
};
double mulDouble(double a, double b)
{
return a * b;
}
BinOp<double> doubleBinOp;
double res = doubleBinOp.do_op<&mulDouble>(4, 5);
Alternatively BinOp could be a class with static method template do_op(...), then called as
double res = BinOp<double>::do_op<&mulDouble>(4, 5);
EDIT
Inspired by comment from 0x2207, here is a functor taking any function with two parameters and convertible values.
struct BinOp
{
template <typename R, typename S, typename T, typename U, typename V> R operator()(R (*binaryOp )(S, T), U u, V v)
{
return binaryOp(u,v);
}
};
double subD(double a, int b)
{
return a-b;
}
int subI(double a, int b)
{
return (int)(a-b);
}
int main()
{
double resD = BinOp()(&subD, 4.03, 3);
int resI = BinOp()(&subI, 4.03, 3);
std::cout << resD << std::endl;
std::cout << resI << std::endl;
return 0;
}
correctly evaluates to double 1.03 and int 1
Edit: Passing the operator as a reference doesnt work. For simplicity, understand it as a function pointer. You just send the pointer, not a reference.
I think you are trying to write something like this.
struct Square
{
double operator()(double number) { return number * number; }
};
template <class Function>
double integrate(Function f, double a, double b, unsigned int intervals)
{
double delta = (b - a) / intervals, sum = 0.0;
while(a < b)
{
sum += f(a) * delta;
a += delta;
}
return sum;
}
.
.
std::cout << "interval : " << i << tab << tab << "intgeration = "
<< integrate(Square(), 0.0, 1.0, 10) << std::endl;