Storing pointer to functions in a map - c++

Can anyone explain why this piece of code is generating the error as shown below. I am trying to store pointer to function in a map. The code would work fine if I keep everything in main() function. Also I would appreciate it if someone shows how to fix it.
#include <boost/variant.hpp>
#include <map>
#include <iostream>
#include <map>
using namespace boost;
class obj {
public:
int num1;
int num2;
std::string s1;
std::string s2;
};
typedef boost::variant<int, float, double, std::string> MultiType;
typedef MultiType(*FnPtr)(obj);
class c {
public:
MultiType add(obj o);
std::map<std::string, FnPtr> metricToFuncMap = { { "i", add } };
};
MultiType c::add(obj o) {
{ MultiType x; x = o.num1 + o.num2; return x; }
}
int main() {
obj o;
//add
MultiType d;
d = 1;
o.num1 = 1;
o.num2 = 2;
//concat
o.s1 = "hello";
o.s2 = "world";
c c;
MultiType x = c.metricToFuncMap["i"](o);
std::cout << get<int>(x);
return 0;
}
Error:
E0289 no instance of constructor "std::map<_Kty, _Ty, _Pr, _Alloc>::map [with _Kty=std::string, _Ty=FnPtr, _Pr=std::less<std::string>, _Alloc=std::allocator<std::pair<const std::string, FnPtr>>]" matches the argument list

Here's a fairly minimal example.
#include <iostream>
#include <functional>
#include <map>
class Foo {
public:
Foo() {}
};
typedef std::function<std::string (const Foo &)> MyFunction;
typedef std::map<std::string, MyFunction> MyMap;
int main(int, char **) {
Foo foo;
MyFunction f = [](const Foo &) { return "Hello"; };
MyMap myMap { {"x", f} };
MyFunction blah = myMap["x"];
std::cout << "Got: " << blah(foo) << "\n";
}
Compile and run:
$ g++ --std=c++17 Blah.cpp -o Blah && Blah
Got: Hello
The advantage of using std::function is that while there's a little overhead, it's far more powerful. You can (as I have) used lambdas. Your lambdas can capture this, meaning you can call non-static member methods. You can also use std::bind(), if you prefer, although I don't care for the syntax and will usually use a lambda instead.

Related

How to Initialize a vector<typeinfo>?

I am able to do this:
std::vector<int> vec = { 1, 2, 3, 4, 5 };
But I am not able to do this:
std::vector<const type_info&> ClassBlackList = { typeid(Class1), typeid(Class2) };
compiler says pointer to reference is illegal
or
std::vector<const type_info> ClassBlackList = { typeid(Class1), typeid(Class2) };
compiler says Error C2338 The C++ Standard forbids containers of const elements because allocator is ill-formed.
or
std::vector<type_info> ClassBlackList = { typeid(Class1), typeid(Class2) };
Compiler says:
Error C2280 'type_info::type_info(const type_info &)': attempting to reference a deleted function
I am able not able to do push_back either.
What is the solution to have a vector or list of type_info?
You can use pointers
std::vector<const std::type_info*> v = { &typeid(Class1), &typeid(Class2) };
This is valid because typeid returns a reference to an object with static storage duration.
You cannot have a vector of references, for several fundamental reasons. C++ simply does not work this way. You can, however, employ std::reference_wrapper to get pretty much the same result:
#include <functional>
#include <vector>
#include <typeinfo>
class A {
};
int main()
{
std::vector<std::reference_wrapper<const std::type_info>> avec;
auto &t=typeid(A);
avec.push_back(t);
const std::type_info &i=avec[0];
return 0;
}
You can't have arrays of references so you could wrap them in std::reference_wrappers:
#include <functional>
#include <typeinfo>
#include <vector>
std::vector<std::reference_wrapper<const std::type_info>> ClassBlackList = {
typeid(Class1),
typeid(Class2)
};
The name ClassBlackList implies that you will search it a lot and also that the elements in the list are to be unique. In that case, you may want to use a std::set instead.
Example:
#include <functional>
#include <iostream>
#include <typeinfo>
#include <set>
struct Class1 {};
struct Class2 {};
struct Class3 {};
struct comp { // a functor to compare reference wrapped type_info's
std::size_t operator()(const std::reference_wrapper<const std::type_info>& lhs,
const std::reference_wrapper<const std::type_info>& rhs) const
{
return std::less<const std::type_info*>{}(&lhs.get(), &rhs.get());
}
};
int main() {
std::set<std::reference_wrapper<const std::type_info>, comp> ClassBlackList = {
typeid(Class1),
typeid(Class2)
};
// try to insert typeid(Class3) twice, it only succeeds the first time
auto[it1, inserted1] = ClassBlackList.insert(typeid(Class3));
std::cout << "inserted: " << inserted1 << '\n';
auto[it2, inserted2] = ClassBlackList.insert(typeid(Class3));
std::cout << "inserted: " << inserted2 << '\n';
}
Output:
inserted: 1
inserted: 0

Boost Hana : Convert Hana Types to std::string's

Does there exist a Boost Hana method for compile-time converting the types of members of a Struct concept to a STL container of std::string's of the typenames?
For example,
MyType t();
std::array<std::string, 3> ls = boost::hana::typesToString(t);
for(std::string x : ls){
std::cout << x << std::endl;
}
Yields "int string bool" to STDOUT,
With
class MyType{
int x;
std::string y;
bool z;
}
The documentation clearly provides methods for getting the members and their values of an instance of a Struct concept, but I haven't found anything there that does this for the types of the members. A simpler task would be to do:
int x;
std::string tName = boost::hana::typeId(x); //tName has value "int"
I've read this post but I'd like to know if there's a clean way out-of-the-box in Hana. Even better would be a way to iterate through the members of the Struct without having to know them by name.
If you are using Clang, Hana has an experimental feature hana::experimental::type_name. This can be used to get the type-names of the members of the struct:
#include <boost/hana.hpp>
#include <boost/hana/experimental/type_name.hpp>
namespace hana = boost::hana;
template <typename Struct>
auto member_type_names() {
constexpr auto accessors = hana::accessors<Struct>();
return hana::transform(
accessors,
hana::compose(
[](auto get) {
using member_type
= std::decay_t<decltype(get(std::declval<Struct>()))>;
return hana::experimental::type_name<member_type>();
},
hana::second
)
);
}
Demo (live on Wandbox):
#include <iostream>
#include <string>
struct MyType {
int a;
std::string b;
float c;
};
BOOST_HANA_ADAPT_STRUCT(MyType, a, b, c);
int main() {
hana::for_each(member_type_names<MyType>(), [](auto name) {
// Note that the type of `name` is a hana::string, not a std::string
std::cout << name.c_str() << '\n';
});
}
Outputs:
int
std::__1::basic_string<char>
float

Variables does not have class type, even though it is defined

I am trying to write a class which defines a std::map. The comparator of the map must be a function pointer. The function pointer can be passed to the class as an argument in class's constructor.
Below is the code I wrote:
#include <iostream>
#include <map>
#include <string>
#include <functional>
typedef std::function<bool(std::string x, std::string y)> StrComparatorFn;
bool FnComparator(std::string x, std::string y) {
return strtoul(x.c_str(), NULL, 0) < strtoul(y.c_str(), NULL, 0);
}
class MyClass {
public:
MyClass(StrComparatorFn fptr):fn_ptr(fptr){};
void Insert() {
my_map.insert(std::pair<std::string, std::string>("1", "one"));
my_map.insert(std::pair<std::string, std::string>("2", "two"));
my_map.insert(std::pair<std::string, std::string>("10", "ten"));
}
void Display() {
for (auto& it : my_map) {
std::cout << it.first.c_str() << "\t => " << it.second.c_str() << "\n";
}
}
private:
StrComparatorFn fn_ptr;
std::map<std::string, std::string, StrComparatorFn> my_map(StrComparatorFn(fn_ptr));
};
int main() {
MyClass c1(&FnComparator);
c1.Insert();
c1.Display();
}
I am getting a compile error in Insert:
error: '((MyClass*)this)->MyClass::my_map' does not have class type
my_map.insert(std::pair<std::string, std::string>("1", "one"));
Any solution to this issue?
That line
std::map<std::string, std::string, StrComparatorFn> my_map(StrComparatorFn(fn_ptr));
has a problem known as the most vexing parse. Basically, everything that can be interpreted as a function, will be:
Foo f(); //f is a function! Not a variable
In your case, my_map is parsed as a declared function without a definition. Using curly braces instead of curved braces will solve the problem, as list initialization can never be interpreted as a function:
std::map<std::string, std::string, StrComparatorFn> my_map{ StrComparatorFn(fn_ptr) };

boost::lambda expression fails to compile because of instantiation of abstract template arg. Any explanation and/or work arounds?

I'm in the process of learning boost::lambda and I've managed to create a situation that I can't resolve with what I know so far.
Apparently in the bowels of boost::lambda, the following example causes the attempted instantiation of abstract class AbstractFoo, and prevents the lambda expression from compiling. The problem is that I don't know why it is trying to instantiate it so I cant try to work around it.
Any boost::lambda experts that can:
give me a clue as to why this is happening?
suggest a work around?
Example:
#include <boost/lambda/bind.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
struct AbstractFoo
{
typedef boost::shared_ptr<AbstractFoo> Ptr;
virtual int it() const = 0;
};
struct Bar : public AbstractFoo
{
typedef boost::shared_ptr<Bar> Ptr;
virtual int it() const { return 3; }
};
typedef AbstractFoo Foo; // Comment this out
//typedef Bar Foo; // and this in to make this example compilable
int main()
{
namespace bll = boost::lambda;
boost::function< bool (const Foo::Ptr &)> func;
func = (bll::protect(bll::bind( &Foo::it, *bll::_1))(bll::_1) == 3);
return 0;
}
This fails to compile (on gcc 4.4.3, boost 1_40) with a monster template error the important part of which seems to be:
error: cannot declare field
‘boost::tuples::cons<AbstractFoo,boost::tuples::null_type>::head’
to be of abstract type ‘AbstractFoo’
because the following virtual functions are pure within ‘AbstractFoo’:
virtual int AbstractFoo::it() const
As you discovered, you can not do that, because the object needs to be copied, but in this case it can not be instantiated because it contains a pure virtual method. The simplest solution is to pass it using a pointer :
#include <boost/lambda/bind.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <iostream>
struct AbstractFoo
{
typedef boost::shared_ptr<AbstractFoo> Ptr;
virtual int it() const = 0;
};
struct Bar : public AbstractFoo
{
typedef boost::shared_ptr<Bar> Ptr;
virtual int it() const { return 3; }
};
typedef AbstractFoo Foo; // Comment this out
//typedef Bar Foo; // and this in to make this example compilable
int main()
{
namespace bll = boost::lambda;
boost::function< bool ( const Foo * )> func;
func = ( bll::protect( bll::bind( &Foo::it, bll::_1 ) )( bll::_1 ) == 3);
//func = bll::bind( &Foo::it, bll::_1 );
Foo::Ptr p( new Bar );
std::cout << std::boolalpha << func( p.get() ) << std::endl;
}
To be more precise, this :
*bll::_1
needs to instantiate and copy object of type AbstractFoo
Riffing off of JVo's answer, the following works around the issue:
func3 = (bll::protect(bll::bind( &Foo::it,
bll::bind( &Foo::Ptr::get,
bll::_1 ))) (bll::_1) == 2);
where
bll::bind( &Foo::Ptr::get, bll::_1)
Pulls out the pointer so that the place holder is not dereffed in line.
From the comments suggesting compiling without error in VS with Boost 1_47 I might guess that the issue has since been fixed in boost, and that it was a sort of bug.

binding to member variables

The following example from boost bind does not work for me:
#include <boost/bind.hpp>
struct A
{
int data;
};
int main()
{
A a;
boost::bind(&A::data, _1)(a) = 1;
}
error: assignment of read-only location 'boost::bind [with A1 = boost::arg<1>, M = int, T = A](&A::data, (<unnamed>::_1, boost::arg<1>())).boost::_bi::bind_t<R, F, L>::operator() [with A1 = A, R = const int&, F = boost::_mfi::dm<int, A>, L = boost::_bi::list1<boost::arg<1> >](((A&)(& a)))'
Am I doing anything wrong? The compiler is g++ 4.4.0
The result type of that bind expression is int (or rather const int&). I think you can override the return type:
boost::bind<int&>(&A::data, _1)(a) = 1;
UncleBens' solution is fine but I thought I'd add that if you use Boost.Lambda the problem disappears:
#include <boost/lambda/bind.hpp>
struct A {
int data;
};
int main() {
namespace bll = boost::lambda;
A a;
bll::bind(&A::data, bll::_1)(a) = 1;
}
And so it does if you use boost::mem_fn:
#include <boost/mem_fn.hpp>
struct A {
int data;
};
int main() {
boost::mem_fn(&A::data)(a) = 1;
}
I'm not sure what you want to do, but does Boost.Bind really overload the assignment operator? If you'd like to assign the value 1 to a.data using the returned function object I think you need to do something like this (also note that "a" needs to be bound by reference):
#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include <cassert>
void foo()
{
A a;
boost::bind(&A::data, _1)(boost::ref(a), 1);
assert(a.data == 1);
}
If you need to use the assignment operator I think that using Boost.Lambda or Boost.Phoenix would be a better choice.