I am trying to make an HTTP class, and I want to use C++11 (not C++14 yet) callbacks via lambdas. I have 2 mockups available, the first one works... but looks ugly. The second one I am aiming, is not compiling (error at the end).
I cannot use std::function, as this is an embedded project, and that template generates a lot of code.
#include <cstring>
class HTTP
{
public:
void get1(const char* url, void* context, void (*callback)(void*, const char*) )
{
callback(context, "");
}
void get2(const char* url, void (*callback)(const char*) )
{
callback("");
}
};
void test()
{
int k;
HTTP http;
http.get1( "http://google.com", &k, [](void* context, const char* s){
int *k = (int*) context;
*k = strlen(s);
});
// this does not compile, looking for other alternatives
http.get2( "http://google.com", [&k](const char* s){
k = strlen(s);
});
}
Error from gcc (xtensa-esp32-elf-g++ (crosstool-NG crosstool-ng-1.22.0-80-g6c4433a) 5.2.0)
HttpRequests.cpp: In function 'void test()':
HttpRequests.cpp:29:6: error: no matching function for call to 'HTTP::get2(const char [18], test()::<lambda(const char*)>)'
});
^
HttpRequests.cpp:11:10: note: candidate: void HTTP::get2(const char*, void (*)(const char*))
void get2(const char* url, void (*callback)(const char*) )
^
HttpRequests.cpp:11:10: note: no known conversion for argument 2 from 'test()::<lambda(const char*)>' to 'void (*)(const char*)'
Lambdas without a capture list are compatible with function pointers, so your first lambda can be passed as an argument to get1(). However, lambdas with a capture list are not convertible to function pointers so it can not be passed to get2().
Lambdas with captures have state but functions can not have state, which is why such lambdas are not convertible to function pointers.
The most common way to have a function accept any lambda (or any callable object) is to use function templates:
class HTTP {
// ...
template <typename Callable>
void get1(const char* url, void* context, Callable callback)
{
callback(context, "");
}
template <typename Callable>
void get2(const char* url, Callable callback)
{
callback("");
}
}
Being function templates, code size might become an issue. If that's not acceptable, then keep your current functions and restrict yourself to never passing lambdas that use captures.
Related
There are two class server and client. client calls method datas() on server. The server responds to the caller with the datalist using datacallback().
I see compile time error in function clientfun2 when serverobj.datas() is called. Please help to fix it.
#include <iostream>
#include <functional>
#include <memory>
#include <string>
#include <vector>
enum Status { SUCCESS, FAILED, UNKOWN };
typedef std::vector<std::string> datalist;
class server {
public:
typedef std::function<void(int, Status, const datalist&)> Callback;
void datas(int request_id, Callback datacallback) {
datalist data; //here data is inserted and set to caller
std::cout << "Invoked datas method\n";
datacallback(123, SUCCESS, data); // sending back to caller
}
};
class client {
public:
void clientfun1(int req_id, Status status, datalist& datas) {
std::cout << "Invoked clientfun1\n";
}
void clientfun2(server serverobj) {
serverobj.datas(123,
std::bind(&client::clientfun1, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3)); /*Here the error comming pls help to fix */
}
};
int main() {
server serverobj;
client clientobj;
clientobj.clientfun2(serverobj);
}
****Error in text format****
bind.cc: In member function ‘void client::clientfun2(server)’:
bind.cc:30:76: error: no matching function for call to ‘server::datas(int, std::_Bind_helper<false, void (client::*)(int, Status, std::vector<std::__cxx11::basic_string<char> >&), client*, const std::_Placeholder<1>&, const std::_Placeholder<2>&, const std::_Placeholder<3>&>::type)’
std::placeholders::_2, std::placeholders::_3));
^
bind.cc:14:8: note: candidate: void server::datas(int, server::Callback)
void datas(int request_id, Callback datacallback) {
^
bind.cc:14:8: note: no known conversion for argument 2 from ‘std::_Bind_helper<false, void (client::*)(int, Status, std::vector<std::__cxx11::basic_string<char> >&), client*, const std::_Placeholder<1>&, const std::_Placeholder<2>&, const std::_Placeholder<3>&>::type {aka std::_Bind<std::_Mem_fn<void (client::*)(int, Status, std::vector<std::__cxx11::basic_string<char> >&)>(client*, std::_Placeholder<1>, std::_Placeholder<2>, std::_Placeholder<3>)>}’ to ‘server::Callback {aka std::function<void(int, Status, const std::vector<std::__cxx11::basic_string<char> >&)>}’
If we take the std::function and the actual function right next to each other it's easy to see a difference (reformatted to show it better):
// vvvvv
typedef std::function<void(int , Status , const datalist&)> Callback;
void clientfun1( int req_id, Status status, datalist& datas) {
// ^^^^^
Now we can see that for the std::function object the last argument is a reference to a constant datalist, while in the clientfun1 function it's not constant.
That makes the two function types different, leading to your error.
I have a structure that should be statically initialized.
struct Option
{ char Option[8];
void (*Handler)(const char* value);
};
void ParseInto(const char* value, const char** target); // string option
void ParseInto(const char* value, int* target, int min, int max); // int option
static int count = 1;
static const char* name;
static const Option OptionMap[] =
{ { "count", [](const char* value) { ParseInto(value, &count, 1, 100); } }
, { "name", [](const char* value) { ParseInto(value, &name); } }
// ...
};
Up to this point it works.
To get rid of repeating the lambda function definition over and over (there are dozens) I want to use a factory like this:
struct Option
{ const char Option[8];
const void (*Handler)(const char* value);
template<typename ...A>
Option(const char (&option)[8], A... args)
: Option(option)
, Handler([args...](const char* value) { ParseInto(value, args...); })
{}
};
static const Option OptionMap[] =
{ { "count", &count, 1, 100 }
, { "name", &name }
};
This does not work for two reasons:
I did not find a type for the first constructor parameter option that perfectly forwards the initialization of the character array. The difficult part is that the length of the assigned array does not match the array length in general.
The even harder part is that the lambda function has a closure and therefore cannot decay to a function pointer. But all parameters are compile time constants. So It should be possible to make the constructor constexpr. However, lambdas seem not to support constexpr at all.
Anyone an idea how to solve this challenge?
The current work around is a variadic macro. Not that pretty, but of course, it works.
Context is C++11. I would not like to upgrade for now, but nevertheless a solution with a newer standard would be appreciated. Problems like this tend to reappear from time to time.
There are some further restrictions by the underlying (old) code. struct Option must be a POD type and the first member must be the character array so a cast from Option* to const char* is valid.
dlopen() is a C function used for dynamically loading shared libraries at runtime. The pattern, in case you're not familiar, is thus:
Call dlopen("libpath", flag) to get a void *handle to the library
Call dlsym(handle, "object_name") to get a void *object to the thing you want from the library
Do what you want with object
Call dlclose (handle) to unload the library.
This is, in C++, a perfect use-case for the so-called aliasing constructor of std::shared_ptr. The pattern becomes:
Construct a std::shared_ptr<void> handle from dlopen("libpath", flag) that will call dlclose() when its destructor is called
Construct a std::shared_ptr<void> object from handle and dlsym(handle, "object_name")
Now we can pass object wherever we want, and completely forget about handle; when object's destructor is called, whenever that happens to be, dlclose() will be called automagically
Brilliant pattern, and it works beautifully. One small problem, though. The pattern above requires a cast from void* to whatever_type_object_is*. If "object_name" refers to a function (which most of the time it does, considering the use-case), this is undefined behavior.
In C, there is a hack to get around this. From the dlopen man page:
// ...
void *handle;
double (*cosine)(double);
// ...
handle = dlopen("libm.so", RTLD_LAZY);
// ...
/* Writing: cosine = double (*)(double)) dlsym(handle, "cos");
would seem more natural, but the C99 standard leaves
casting from "void *" to a function pointer undefined.
The assignment used below is the POSIX.1-2003 (Technical
Corrigendum 1) workaround; see the Rationale for the
POSIX specification of dlsym(). */
*(void **) (&cosine) = dlsym(handle, "cos");
// ...
which obviously works just fine, in C. But is there an easy way to do this with std::shared_ptr?
The pattern above requires a cast from void* to whatever_type_object_is*. If "object_name" refers to a function (which most of the time it does, considering the use-case), this is undefined behavior.
Well this is not entirely true, at least in C++ it is just conditionally-supported.
5.2.10.8 says:
Converting a function pointer to an object pointer type or vice versa is conditionally-supported. The meaning
of such a conversion is implementation-defined, except that if an implementation supports conversions in
both directions, converting a prvalue of one type to the other type and back, possibly with different cvqualification,
shall yield the original pointer value.
So assuming that what dlsym does internally is casting a function pointer to a void*, I believe that you are ok if you just cast it back to a function pointer.
Something like this?
struct dlib
{
public:
template<class T>
std::shared_ptr<T> sym(const char* name) const {
if (!handle) return {};
void* sym = dlsym(handle->get(), name);
if (!sym) return {};
return {reinterpret_cast<T*>(sym), handle};
}
// returns a smart pointer pointing at a function for name:
template<class Sig>
std::shared_ptr<Sig*> pfunc(const char* name) const {
if (!handle) return {};
void* sym = dlsym(handle->get(), name);
if (!sym) return {};
Sig* ret = 0;
// apparently approved hack to convert void* to function pointer
// in some silly compilers:
*reinterpret_cast<void**>(&ret) = sym;
return {ret, handle};
}
// returns a std::function<Sig> for a name:
template<class Sig>
std::function<Sig> function(const char* name) const {
// shared pointer to a function pointer:
auto pf = pfunc(name);
if (!pf) return {};
return [pf=std::move(pf)](auto&&...args)->decltype(auto){
return (*pf)(decltype(args)(args)...);
};
}
dlib() = default;
dlib(dlib const&)=default;
dlib(dlib &&)=default;
dlib& operator=(dlib const&)=default;
dlib& operator=(dlib &&)=default;
dlib(const char* name, int flag) {
void* h = dlopen(name, flag);
if (h)
{
// set handle to cleanup the dlopen:
handle=std::shared_ptr<void>(
h,
[](void* handle){
int r = dlclose(handle);
ASSERT(r==0);
}
);
}
}
explicit operator bool() const { return (bool)handle; }
private:
std::shared_ptr<void> handle;
};
I doubt that hack is needed. As #sbabbi noted, the round-trip to void* is conditionally supported. On a system using dlsym to return function pointers, it better be supported.
You can make a struct to have your pointer to function and handle to library:
template<typename T>
struct dlsymbol {
dlsymbol( const std::string &name, std::shared_ptr<void> handle ) :
m_handle( std::move( handle ) )
{
*(void **)(&m_func) = dlsym( handle.get(), name.c_str );
}
std::shared_ptr<void> m_handle;
T *m_func;
};
auto cosine = std::make_shared<dlsymbol<double(double)>>( "cos", handle );
auto d = cosine->m_func( 1.0 );
I did not compile it, but I think it is sufficient to show the idea.
Consider this simple code snippet:
static void Foo(std::string&& arg) {
printf("(universal reference) = %s\n", arg.c_str());
}
static void Foo(const std::string&& arg) {
printf("(const universal reference) = %s\n", arg.c_str());
}
static void Foo(std::string& arg) {
printf("(reference) = %s\n", arg.c_str());
}
static void Foo(const std::string& arg) {
printf("(const reference) = %s\n", arg.c_str());
}
int main(int argc, const char * argv[]) {
std::string value{"value"};
const std::string const_value{"const_value"};
Foo(value);
Foo(const_value);
Foo(std::string("temporary"));
Foo("litteral");
}
The resulting output under Clang is:
(reference) = value
(const reference) = const_value
(universal reference) = temporary
(universal reference) = literal
Why is the value case not using the universal reference version of the function? I thought one of the key benefits of universal references was that they could accept both lvalues and rvalues?
PS: It's not possible to force it either:
static void Foo(std::string&& arg) {
printf("(universal reference) = %s\n", arg.c_str());
}
//static void Foo(const std::string&& arg) {
// printf("(const universal reference) = %s\n", arg.c_str());
//}
//
//static void Foo(std::string& arg) {
// printf("(reference) = %s\n", arg.c_str());
//}
//
//static void Foo(const std::string& arg) {
// printf("(const reference) = %s\n", arg.c_str());
//}
int main(int argc, const char * argv[]) {
std::string value{"value"};
const std::string const_value{"const_value"};
Foo(value); <--- FAILS COMPILING: No matching function for call to 'Foo'
// Foo(const_value);
Foo(std::string("temporary"));
Foo("literal");
}
UPDATE: It appears "universal references" are only for templates, not regular functions, which explains why the above is not working as I was expecting.
However here's a version using a templated function:
template<typename T>
static void Foo(T&& arg) {
printf("%s\n", arg.c_str());
}
int main(int argc, const char * argv[]) {
std::string value{"value"};
const std::string const_value{"const_value"};
Foo<std::string>(value); <--- FAILS COMPILING: No matching function for call to 'Foo'
Foo<std::string>(const_value); <--- FAILS COMPILING: No matching function for call to 'Foo'
Foo<std::string>(std::string("temporary"));
Foo<std::string>("literal");
}
Why is the value case still not working through universal references (I understand why the const_value case is not)?
UPDATE: For reference, here's the final version that works with both lvalues and rvalues:
template<typename T>
static void Foo(T&& arg) {
printf("%s\n", arg.c_str());
}
int main(int argc, const char * argv[]) {
std::string value{"value"};
const std::string const_value{"const_value"};
Foo(value);
Foo(const_value);
Foo(std::string("temporary"));
//Foo("literal"); <--- Cannot work anyway since template is instantiated with arg as a const char*
}
Those are not "universal references". Those are merely rvalue references.
A "universal reference" (FYI: this term has fallen out of disfavor) refers explicitly to an rvalue reference applied directly to a fully template deduced type (as opposed to something like vector<T> &&v, which is only partially deduced) when using template argument deduction. None of your functions are template functions, so they behave like regular rvalue references.
foo(value) calls the lvalue reference version because variables passed as parameters bind to lvalue reference parameter types.
However here's a version using a templated function:
None of those functions use template argument deduction. You're explicitly specifying the template parameter directly, not allowing it to be deduced by the arguments you call it with.
Or more to the point:
Foo<std::string>(value);
Foo(value);
Foo<std::string>(std::string("temporary"));
Foo(std::string("temporary"));
In the first case, T is std::string, because you specified it to be so. Therefore, Foo<std::string> takes a std::string&&. value cannot bind to an rvalue reference, so it's a compile error.
In the second case, T is deduced through template argument deduction based on the argument expression. value is an lvalue, so T is deduced as std::string&. Therefore, Foo in this case takes a std::string& &&, which devolves down to std::string&.
In the third case, T is std::string. Therefore again, Foo<std::string> takes a std::string&&. A temporary can bind to an rvalue reference just fine, so it does so.
In the fourth case, T is deduced through template argument deduction based on the argument expression. The expression is a std::string temporary prvalue, so T is deduced as std::string (note the lack of references). So Foo in this case takes a std::string&&.
I'm having a problem with a part of a larger program where something that I'd say is not ambiguous is considered ambiguous by both g++ and clang++
#include <functional>
#include <string>
struct Foo {
Foo(int) {}
Foo(std::string) {}
operator int () const { return 42; }
operator std::string () const { return ""; }
void foo(std::function<void(Foo&, int)>f);
void foo(std::function<void(const Foo&, int)>f) const; // xxx
void foo(std::function<void(const std::string&, Foo&)>f);
void foo(std::function<void(const std::string&, const Foo&)>f) const;
void bar() const {
this->foo([](const Foo&, int){}); // xxx
}
};
I'd expect the invocation of ::foo made in bar to be unambiguously resolved to the const version marked with xxx, while instead both compilers complain that the overload resolution is ambiguous:
g++ -std=c++11 -c -Wall amb.cpp
amb.cpp: In member function ‘void Foo::bar() const’:
amb.cpp:18:40: error: call of overloaded ‘foo(Foo::bar() const::<lambda(const Foo&, int)>)’ is ambiguous
this->foo([](const Foo&, int){});
^
amb.cpp:12:10: note: candidate: void Foo::foo(std::function<void(const Foo&, int)>) const
void foo(std::function<void(const Foo&, int)>f) const;
^
amb.cpp:15:10: note: candidate: void Foo::foo(std::function<void(const std::basic_string<char>&, const Foo&)>) const
void foo(std::function<void(const std::string&, const Foo&)>f) const;
^
Why it's not clear which version I want to call? How can I work around this problem?
The problem is that some type information is lost when passing from a lambda to an std::function object (in particular the argument types).
To be more specific for example a function accepting std::function<void(int)> and another accepting std::function<void(double)> are considered equally good overloads for a void lambda accepting an int argument.
For example with
#include <functional>
void foo(std::function<void(int)>f);
void foo(std::function<void(double)>f);
void bar() {
foo([](int){});
}
the call in bar is considered ambiguous
amb2.cpp: In function ‘void bar()’:
amb2.cpp:8:18: error: call of overloaded ‘foo(bar()::<lambda(int)>)’ is ambiguous
foo([](int){});
^
amb2.cpp:4:6: note: candidate: void foo(std::function<void(int)>)
void foo(std::function<void(int)>f);
^
amb2.cpp:5:6: note: candidate: void foo(std::function<void(double)>)
void foo(std::function<void(double)>f);
^
Workaround
A solution is to explicitly create the proper std::function object with:
this->foo(std::function<void(const Foo&, int)>([](const Foo&, int){}));
You could make the constructor taking the std::string argument explicit. That way the compiler can't make an implicit conversion from std::string to Foo.