C++ passing a callback with a range parameter - c++

I'd like to achieve something like this:
void mycallback(auto v)
{
for (auto x : v) {
std::cout << x << '\n';
}
}
void f(auto cb) {
cb(std::vector<int>{44,55,66});
cb(std::vector<int>{123, 234, 345} | std::ranges::views::drop(1));
}
void run()
{
f(mycallback);
f([](auto v) {
for (auto x : v) {
std::cout << x << '\n';
}
});
}
mycallback() should accept a C++20 range of integers. To be more specific I'll need owning_view with miscellaneous filters. So it is a template. I need to pass this callback to f() which can call the callback with different range types. I need it to accept mainly lambdas. The f() can't be instantiated because compiler doesn't know how to deduce the type since the callback instantiation is required inside the f() function.
test.cpp:34:6: note: template argument deduction/substitution failed:
test.cpp:41:6: note: couldn’t deduce template parameter ‘auto:25’
41 | f(mycallback);
Is there some lazy instantiation option?

You could pass a lambda that calls the callback.
And for the callback to work with ranges, change the signature to auto&& v, so that it can accept r-value references as arguments.
[Demo]
#include <iostream>
#include <ranges>
#include <vector>
void mycallback(auto&& v) {
for (auto x : v) {
std::cout << x << " ";
}
std::cout << "\n";
}
void f(auto cb) {
cb(std::vector<int>{44, 55, 66});
cb(std::vector<int>{123, 234, 345} | std::ranges::views::drop(1));
}
int main() {
f([](auto&& v) { return mycallback(v); });
}

mycallback is very similar to function template due to the auto parameter.
And in this line:
f(mycallback);
The compiler doesn't know with which type (for the auto parameter) to instantiate it.
Instead you can make it a functor - i.e. a class with operator().
You can also make the call operator accept v bv auto && for types that will otherwise be copied.
Example:
#include <vector>
#include <iostream>
#include <ranges>
class mycallback
{
public:
void operator()(auto && v) const
{
for (auto const & x : v) {
std::cout << x << '\n';
}
}
};
void f(auto cb) {
cb(std::vector<int>{44, 55, 66});
cb(std::vector<int>{123, 234, 345} | std::ranges::views::drop(1));
}
int main()
{
f(mycallback{});
}
Output:
44
55
66
234
345

Related

How to inspect if a GENERIC lambda is callable with a certain input arguement type?

Is there anyway to check if a generic lambda is callable with a specific type?!
#include <iostream>
#include <type_traits>
struct A { void DoAStuff() {} };
struct B { void DoBStuff() {} };
int main() {
auto fn = [](auto& arg) { arg.DoAStuff(); };
std::cout << std::boolalpha << std::is_invocable<decltype(fn), A&>::value << std::endl;
std::cout << std::boolalpha << std::is_invocable<decltype(fn), B&>::value << std::endl;
}
this code doesn't compile and I understand why. but I am wondering if there is anyway to get something like this to work? like we can define a class than when passed the lambda, return false instead of compilation error
error: no member named 'DoAStuff' in 'B'

Did std::bind implement std::ref and std::cref to disambiguate the function call?

I know that I shouldn't overload a function for just parameters differ only in one of them passed by copy and the other by reference:
void foo(int x)
{
cout << "in foo(int x) x: " << x << endl;
}
void foo(int& x)
{
cout << "in foo(int& x) x: " << x << endl;
}
int main()
{
int a = 1;
foo(5); // ok as long as there is one best match foo(int)
foo(a); // error: two best candidates so the call is ambiguous
//foo(std::move(a));
//foo(std::ref(an)); // why also this doesn't work?
}
So a code that uses std::bind can be like this:
std::ostream& printVec(std::ostream& out, const std::vector<int> v)
{
for (auto i : v)
out << i << ", ";
return out;
}
int main()
{
//auto func = std::bind(std::cout, std::placeholders::_1); // error: stream objects cannot be passed by value
auto func = std::bind(std::ref(std::cout), std::placeholders::_1); // ok.
}
So std::ref here to ensure passing by reference rather than by value to avoid ambiguity?
* The thing that matters me: Does std::bind() implemented some wrapper to overcome this issue?
Why I can't use std::ref in my example to help the compiler in function matching?
Now that you know passing by value and reference are ambiguous when overload resolution tries to compare them for choosing a best viable function, let's answer how would you use std::ref (or std::cref) to differentiate between pass-by-value and pass-by-reference.
It turns out to be ... pretty simple. Just write the overloads such that one accepts a int, and the other accepts a std::reference_wrapper<int>:
#include <functional>
#include <iostream>
void foo(int x) {
std::cout << "Passed by value.\n";
}
void foo(std::reference_wrapper<int> x) {
std::cout << "Passed by reference.\n";
int& ref_x = x;
ref_x = 42;
/* Do whatever you want with ref_x. */
}
int main() {
int x = 0;
foo(x);
foo(std::ref(x));
std::cout << x << "\n";
return 0;
}
Output:
Passed by value.
Passed by reference.
42
The function pass the argument by value by default. If you want to pass by reference, use std::ref explicitly.
Now let's answer your second question: how does std::bind deal with this type of scenario. Here is a simple demo I have created:
#include <functional>
#include <type_traits>
#include <iostream>
template <typename T>
struct Storage {
T data;
};
template <typename T>
struct unwrap_reference {
using type = T;
};
template <typename T>
struct unwrap_reference<std::reference_wrapper<T>> {
using type = std::add_lvalue_reference_t<T>;
};
template <typename T>
using transform_to_storage_type = Storage<typename unwrap_reference<std::decay_t<T>>::type>;
template <typename T>
auto make_storage(T&& obj) -> transform_to_storage_type<T> {
return transform_to_storage_type<T> { std::forward<T>(obj) };
}
int main() {
int a = 0, b = 0, c = 0;
auto storage_a = make_storage(a);
auto storage_b = make_storage(std::ref(b));
auto storage_c = make_storage(std::cref(c));
storage_a.data = 42;
storage_b.data = 42;
// storage_c.data = 42; // Compile error: Cannot modify const.
// 0 42 0
std::cout << a << " " << b << " " << c << "\n";
return 0;
}
It is not std::bind, but the method used is similar (it's also similar to std::make_tuple, which has the same semantic). make_storage by default copies the parameter, unless you explicitly use std::ref.
As you can see, std::ref is not magic. You need to do something extra for it to work, which in our case is to first decay the type (all references are removed in this process), and then check whether the final type is a reference_wrapper or not; if it is, unwrap it.

Templated optional argument constructor with void parameter callback

I'm looking to write a class such than it can store a piece of data and pass it through a lambda. I'd also like to be able to have an option to not pass a value in the constructor and have the constructor generate with a lambda without a parameter. I'm OK, with still having a piece of data on the class.
I've tried using optionals and using a default template parameter of type void but couldn't get it to work. The closest thing I got was this.
template<class T = void>
class X {
std::optional<T> d; // data present or garbage data
std::function<void(T)> f; // parameter = type of data or void if no arg provided
public:
X(std::optional<T> a, std::function<void(T)> b) : d{a}, f{b} {}
a() {
if (d.has_value()) f(d);
else f();
}
}
Hoping the constructor work like
X(5, [&](int x) {}); // where the int type is implied from the constructor
OR
X([&](){}); // where the lack of parameter implies the function parameter is void
Thank you.
EDIT: provided an example of an attempt to generate the class myself
EDIT 2: attempted to clarified the need to store and call the function at a later date.
You can use something like this:
#include <iostream>
#include <functional>
void foo() {
std::cout << "void foo()" << std::endl;
}
void bar(int x) {
std::cout << "void foo(int x) with parameter x = " << x << std::endl;
}
class X {
public:
template <typename F, typename ... Args>
X(F&& f, Args&&... args) {
std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}
};
int main() {
X a(foo);
X b(bar, 1);
X c([&](int x, int y) {
std::cout << "lambda with parameters x = " << x << " y = " << y << std::endl;
}, 1, 2);
return 0;
}
Check live
Basically, by using provided class X you can send whatever function you want (free function, lambda, etc.) and pass any number of parameters you want.
UPDATE
According to the OP's comment and wish to store the lambda and arguments to a class member and call it later, here is the updated code:
#include <iostream>
#include <tuple>
#include <functional>
template <typename F, typename ... Args>
class X {
public:
X(F&& f, Args&&... args)
: f_(std::forward<F>(f)),
args_(std::forward<Args>(args)...)
{}
void call() {
std::apply(f_, args_);
}
private:
F f_;
std::tuple<Args...> args_;
};
int main() {
X c([&](int x, int y) {
std::cout << "lambda with parameters x = " << x << " y = " << y << std::endl;
}, 1, 2);
c.call();
return 0;
}
Check live

Safe Callback Provider (SFINAE, std::forward and overload resolution)

I'm working on a mechanism for creating "safe" callbacks, that won't cause undefined behavior when called after their parent object has been destroyed. The class should be generic enough to be able to wrap any callback, with void(...) callbacks simply being executed or not, depending on the status of the object that they are bound to, and callbacks that return a value returning a boost::optional with the returned value, if executed, or boost::none if not executed.The implementation is almost complete, but there are 2 things that make me worried that I don't fully understand my code...
If line 19 is uncommented and 18 commented out, the template won't compile - is this merely a syntactic problem that can be solved, or am I trying to use the result_of mechanism incorrectly (does the std::forward there change the semantics or is it superfluous?)
If line 88 is uncommented and 89 commented out, the compilation results in failure due to ambiguousness of the function call to fun, which I don't quite understand - it seems to me that fun(int&&) is an exact match, so why does the compiler complain of ambiguousness with fun(int) version?
If there are other subtle (or gross) errors, please comment as well.
Thanks.
#include <iostream>
#include <string>
#include <type_traits>
#include <utility>
#include <memory>
#include <boost/optional.hpp>
template<class Func>
class SafeCallback
{
public:
SafeCallback(std::shared_ptr<bool> guard, const Func& callback)
: guard_(guard)
, callback_(callback)
{}
template<class... Args>
// auto operator()(Args&&... args) -> typename std::enable_if<std::is_void<typename std::result_of<Func(std::forward<Args>(args)...)>::type>::value, // won't compile with: 19:91: error: invalid use of template-name 'std::result_of' without an argument list
auto operator()(Args&&... args) -> typename std::enable_if<std::is_void<typename std::result_of<Func(Args...)>::type>::value,
void>::type
{
std::cout << "trying void callback" << std::endl;
if(guard_.lock())
{
std::cout << "callback is still alive :)" << std::endl;
callback_(std::forward<Args>(args)...);
return;
}
std::cout << "uh-oh, callback is dead!" << std::endl;
}
template<class... Args>
auto operator()(Args&&... args) -> typename std::enable_if<!std::is_void<typename std::result_of<Func(Args...)>::type>::value,
boost::optional<typename std::result_of<Func(Args...)>::type>>::type
{
std::cout << "trying non-void callback" << std::endl;
if(guard_.lock())
{
std::cout << "callback is still alive :)" << std::endl;
return callback_(std::forward<Args>(args)...);
}
std::cout << "uh-oh, callback is dead!" << std::endl;
return boost::none;
}
bool isAlive()
{
return guard_.lock();
}
private:
std::weak_ptr<bool> guard_;
Func callback_;
};
class SafeCallbackProvider
{
public:
SafeCallbackProvider()
: guard_(new bool(true))
{}
virtual ~SafeCallbackProvider() = default;
template<class Func>
SafeCallback<Func> makeSafeCallback(const Func& callback)
{
return SafeCallback<Func>(guard_, callback);
}
private:
std::shared_ptr<bool> guard_;
};
struct A : SafeCallbackProvider
{
void fun()
{
std::cout << "---this is fun---" << std::endl;
}
int fun(int&& i)
{
std::cout << "&& this is && " << i << " && fun &&" << std::endl;
return i;
}
// int fun(int i) // fails to compile with: 123:48: error: call of overloaded 'fun(int)' is ambiguous
int fun(int& i)
{
std::cout << "---this is ---" << i << "--- fun---" << std::endl;
return i;
}
};
int main()
{
A* a= new A;
auto cb = a->makeSafeCallback(
[&]()
{
a->fun();
});
cb();
delete a;
cb();
std::cout << "\n----------\n\n";
A* a2= new A;
auto cb2 = a2->makeSafeCallback(
[&](int i)
{
return a2->fun(i);
});
cb2(5);
delete a2;
cb2(5);
std::cout << "\n----------\n\n";
A* a3= new A;
auto cb3 = a3->makeSafeCallback(
[&](int&& i)
{
return a3->fun(std::forward<int>(i));
});
cb3(5);
delete a3;
cb3(5);
return 0;
}
Note: this only answers the first question, because I apparently have the attention span of a fly. More coming soon.
std::result_of essentially performs some magic based on a function type that looks like a function call. In the line that works:
typename std::result_of<Func(Args...)>::type
This is the intended use, simulating the call of an instance of Func with values of types Args.... On the other hand:
typename std::result_of<Func(std::forward<Args>(args)...)>::type
This expands Args and args into a group of values, which then form a chain of ,-operators inside a functoin-style cast to Func. The whole thing is an expression instead of the type std::result_of expects.
It looks like you're halfway to using decltype instead, which would look like:
decltype(std::declval<Func&>()(std::forward<Args>(args)...))
... or, if you can be bothered to move it underneath callback_'s declaration:
decltype(callback_(std::forward<Args>(args)...))
Rules of Overloading are that .
Signature of function should be different.
In both the case compiler is finding same signature, try to change the signature and see the result.

std::bind and overloaded function

Please refer the following code snippet. I want to use the std::bind for overloaded function foobar. It calls only the method with no arguments.
#include <functional>
#include <iostream>
class Client
{
public :
void foobar(){std::cout << "no argument" << std::endl;}
void foobar(int){std::cout << "int argument" << std::endl;}
void foobar(double){std::cout << "double argument" << std::endl;}
};
int main()
{
Client cl;
//! This works
auto a1 = std::bind(static_cast<void(Client::*)(void)>(&Client::foobar),cl);
a1();
//! This does not
auto a2= [&](int)
{
std::bind(static_cast<void(Client::*)(int)>(&Client::foobar),cl);
};
a2(5);
return 0;
}
You need to use placeholders for the unbound arguments:
auto a2 = std::bind(static_cast<void(Client::*)(int)>(&Client::foobar), cl,
std::placeholders::_1);
a2(5);
You can also perform the binding with a lambda capture (note that this is binds cl by reference, not by value):
auto a2 = [&](int i) { cl.foobar(i); };