What's wrong of this use of boost::lambda::bind? - c++

I'm trying to use boost::lambda::bind() to define a predicate that I pass to the find_if algorithm in Boost.Range. Specifically, I want to search a vector of structures to find the first entry where a particular member has a specified value. My example is as follows:
#include <boost/lambda/bind.hpp>
#include <boost/range/algorithm/find_if.hpp>
#include <vector>
using namespace std;
using namespace boost;
using namespace boost::lambda;
struct foo
{
string s;
int x;
};
int main()
{
// create list and add a couple entries
vector<foo> fooList;
foo f1 = {"abc", 1};
foo f2 = {"def", 2};
fooList.push_back(f1);
fooList.push_back(f2);
// search for a value with the desired member
// fails with a compile error!
range_iterator<vector<foo> > it = find_if(fooList, boost::lambda::bind(&foo::s, _1) == string("abc"));
return 0;
}
When I try to compile this (under gcc 4.7.2), I get the typical spew of template instantiation errors, indicating that there was no operator== found that is compatible with the type returned by bind() and a const char []. I've tried this with other types also, such as int, with the same result.
I must be missing some small detail of bind() usage, but I can't see it; it seems like this sort of thing should work based upon the documentation. Am I wrong there?
Edit: Here is the first part of the compiler output:
test.cc:24:92: error: no match for ‘operator==’ in ‘boost::lambda::bind(const Arg1&, const Arg2&) [with Arg1 = std::basic_string<char> foo::*; Arg2 = boost::lambda::lambda_functor<boost::lambda::placeholder<1> >; typename boost::lambda::detail::bind_tuple_mapper<const Arg1, const Arg2>::type = boost::tuples::tuple<std::basic_string<char> foo::* const, const boost::lambda::lambda_functor<boost::lambda::placeholder<1> >, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type>]((* & boost::lambda::{anonymous}::_1)) == "abc"’

Turns out that I wasn't including the required headers. It appears that <boost/lambda/bind.hpp> only brings in bind functionality, and the operator overloads for the resulting type are not included. If I add #include <boost/lambda/lambda.hpp> to the above, then it resolves the compiler error that I referenced. The final revised code (fixing another error in the type of the return value from find_if()) is as follows:
#include <boost/lambda/bind.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/range/algorithm/find_if.hpp>
#include <string>
#include <vector>
using namespace std;
using namespace boost;
using namespace boost::lambda;
struct foo
{
string s;
int x;
};
int main()
{
// create list and add a couple entries
vector<foo> fooList;
foo f1 = {"abc", 1};
foo f2 = {"def", 2};
fooList.push_back(f1);
fooList.push_back(f2);
// search for a value with the desired member
typename range_iterator<vector<foo> >::type it = find_if(fooList, bind(&foo::s, _1) == "abc");
return 0;
}

Related

How to use variants as the key in unordered_map?

How can I use variants as the key in unordered_map?
For example, I'd like to make the following code work.
using VariantType = std::variant<int, std::string, unsigned int>;
std::unordered_map<VariantType, int, $some_hash_function$> m;
How do I implement $some_hash_function$?
There is already a hash template specialization for variant:
http://en.cppreference.com/w/cpp/utility/variant/hash
The only condition is that every type in the variant must have a hash function:
The specialization std::hash<std::variant<Types...>> is enabled (see std::hash) if every specialization in std::hash<std::remove_const_t<Types>>... is enabled, and is disabled otherwise.
But all your variant types have default hashes so, for your variant types, it compiles without the third parameter because the standard hash works. However, if you had a type in your variant that did not have a hash function (or an == operator) then it would fail to compile with this error:
error: static assertion failed: hash function must be invocable with an argument of key type
So back to your question:
When the variant types have hash functions:
#include <variant>
#include <unordered_map>
#include <string>
#include <iostream>
using VariantType = std::variant<int, std::string, unsigned int>;
std::unordered_map<VariantType, int> m =
{
{1, 1},
{2u, 2},
{std::string("string"),3}
};
int main()
{
VariantType v = std::string{"string"};
std::cout << m[v];
}
You get this output:
Program returned: 0
Program stdout
3
And when not all the variant types have hash functions:
#include <variant>
#include <unordered_map>
#include <string>
#include <iostream>
class UnhashedClass {};
using VariantType = std::variant<UnhashedClass, int, std::string>;
std::unordered_map<VariantType, int> m =
{
{1, 1},
{2u, 2},
{std::string("string"),3}
};
int main()
{
VariantType v = std::string{"string"};
std::cout << m[v];
}
You get this output:
Could not execute the program
Compiler returned: 1
Compiler stderr
...
error: static assertion failed: hash function must be invocable with an argument of key type
...
You can try it yourself here:
https://godbolt.org/z/bnzcE9

compilation fails for nested boost::variant of fusion pairs

Following code fails to compile on gcc 4.9.2 with boost 1.64:
#include <boost/fusion/support/pair.hpp>
#include <boost/mpl/int.hpp>
#include <boost/variant.hpp>
#include <string>
namespace bf = boost::fusion;
namespace bm = boost::mpl;
int
main(int argc, char** argv)
{
using L2_t = boost::variant<bf::pair<bm::int_<1>,char>>;
using L1_t = boost::variant<bf::pair<bm::int_<1>,L2_t>>;
L1_t result;
}
while I think, it's legal.
Error message starts with:
/home/vagrant/.conan/data/boost/1.64.0/conaned/master/package/b06f2a498643ee1d889c6aea8e0e21d3cac9f57c/include/boost/fusion/support/pair.hpp: In instantiation of ‘constexpr boost::fusion::pai
r<First, Second>::pair(const boost::fusion::pair<First, Second2>&) [with Second2 = boost::variant<boost::fusion::pair<mpl_::int_<1>, char> >; First = mpl_::int_<1>; Second = char]’: /home/vagrant/.conan/data/boost/1.64.0/conaned/master/package/b06f2a498643ee1d889c6aea8e0e21d3cac9f57c/include/boost/type_traits/is_constructible.hpp:33:45: required by substitution of ‘tem
plate<class T, class Arg, class> static boost::type_traits::yes_type boost::detail::is_constructible_imp::test1(int) [with T = boost::fusion::pair<mpl_::int_<1>, char>; Arg = const boost::fus
ion::pair<mpl_::int_<1>, boost::variant<boost::fusion::pair<mpl_::int_<1>, char> > >&; <template-parameter-1-3> = <missing>]’
Compilation succeeds when I change one of pair's key value to make them different.
How to solve this problem? I use those pair key values to serialize/deserialize, so having them same values on different levels is important for me.
As xaxxon said, this works starting with gcc 5.2.

Boost.Type_erasure: member function return _self

I want to use Boost.Type_erasure on a member function that returns the type itself. With _self as the return type, it is OK. However, when the return type changes to pair<_self, some_type>, an error occurs. The following code reproduces the problem.
#include <utility>
#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/member.hpp>
BOOST_TYPE_ERASURE_MEMBER((HasTest1), Test1, 0)
BOOST_TYPE_ERASURE_MEMBER((HasTest2), Test2, 0)
using boost::type_erasure::_self;
using std::pair;
using Type1 = boost::type_erasure::any<
boost::mpl::vector<
boost::type_erasure::copy_constructible<>,
HasTest1<_self(), _self const>
>,
_self
>;
using Type2 = boost::type_erasure::any<
boost::mpl::vector<
boost::type_erasure::copy_constructible<>,
HasTest2<pair<_self, int>(), _self const>
>,
_self
>;
int main() {
struct test {
test Test1() const { throw; }
pair<test, int> Test2() const { throw; }
};
Type1{ test{} };// OK
Type2{ test{} };// Error
// Type2{ test{} }.Test2().first.Test2();// Expected to work
}
How can I fix this problem without using return arguments?
Sample error message:
main.cpp:6:1: error: no viable conversion from 'pair<test, [...]>' to 'pair<boost::type_erasure::_self, [...]>'
BOOST_TYPE_ERASURE_MEMBER((HasTest2), Test2, 0)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The following is the response from Steven Watanabe.
The library can only handle placeholders at the top level. There is no way to handle the general case, X<_self> automatically. This is essentially the same as covariant return types for virtual functions, which have similar restrictions.
To get the effect that you want, you'll need to define the concept_interface manually.
Alternatively, I use boost::any to work around.
#include <utility>
#include <boost/any.hpp>
#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/member.hpp>
BOOST_TYPE_ERASURE_MEMBER((HasTest), Test, 0)
using Type = boost::type_erasure::any<
boost::mpl::vector<
boost::type_erasure::copy_constructible<>,
HasTest<boost::any()>
>
>;
int main() {
struct TestType {
auto Test() {
return std::make_pair(0, Type{ *this });
}
};
auto obj = Type{ TestType{} }.Test();
boost::any_cast<std::pair<int, Type>&>(obj).second.Test();
}

How to declare a self-referential template typedef

Here's a small example which is substantially similar to what I'm trying to do:
#include <boost/variant/variant.hpp>
#include <boost/variant/recursive_wrapper.hpp>
#include <utility>
#include <vector>
struct foo {
const char * str;
};
typedef std::pair<float, float> fpair;
//typedef std::vector<boost::variant<int, fpair, foo, vlist>> vlist;
// ^ No...
//typedef std::vector<boost::variant<int, fpair, foo, boost::recursive_wrapper<vlist>>> vlist;
// ^ No...
//template <typename T = vlist<T> >
//using vlist = std::vector<boost::variant<int, fpair, foo, boost::recursive_wrapper<vlist>>>;
// ^ No...
template <typename T = vlist<T> >
using vlist = std::vector<boost::variant<int, fpair, foo, boost::recursive_wrapper<T>>>;
// Still no?
int main () {
std::cout << "Hello world\n";
}
The error I get with gcc 4.8 is:
test.cpp:12:33: error: expected nested-name-specifier before ‘vlist’
template <typename T = typename vlist<T>>
^
test.cpp:12:33: error: expected ‘>’ before ‘vlist’
The error with clang 3.6 is:
test.cpp:12:24: error: unknown type name 'vlist'
template <typename T = vlist<T>>
^
test.cpp:12:29: error: expected ',' or '>' in template-parameter-list
template <typename T = vlist<T>>
^
test.cpp:12:32: error: expected unqualified-id
template <typename T = vlist<T>>
^
3 errors generated.
(Edit: actually these errors are from slightly different versions of the above code, but they all give quite similar messages)
I looked at these earlier, slightly different questions, I'm still stumped:
How to declare a self referencing template type
How to properly declare a self-referencing template type?
Boost Fusion adapt declaration for a templated self referential structure
Does anyone know a trick for this, or is there some reason I'm not aware of that the compiler inherently isn't able to do this?
I believe you just want boost::make_recursive_variant:
#include <boost/variant/variant.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <utility>
#include <vector>
struct foo {
const char* str;
};
typedef std::pair<float, float> fpair;
typedef boost::make_recursive_variant<
int,
fpair,
foo,
std::vector<boost::recursive_variant_>
>::type vlist;
int main() {
std::vector<vlist> vec;
vec.push_back(4);
vec.push_back(fpair{1.0f, 2.0f});
vlist v2(vec);
}

Boost sub_range with for_each; why am I getting a const-reference?

On OS X Mavericks, using boost 1.55.0 and clang-500.2.79 (based on LLVM 3.3svn), I'm trying to iterate over a sub-range in a std::map using boost::for_each and boost:sub_range. In my function-object, I expect to receive a std::pair &. Instead, I seem to receive a const std::pair &. Why?
#include <map>
#include <boost/range/algorithm.hpp>
#include <boost/range/sub_range.hpp>
using std::map;
using std::begin;
using std::end;
using std::pair;
using boost::for_each;
using boost::sub_range;
int main()
{
map<int, int> myMap;
sub_range<decltype(myMap)> s{
begin(myMap),
end(myMap)
};
auto f1 = [&](const pair<int, int> &) {
};
for_each(s, f1); // Compiles fine
auto f2 = [&](pair<int, int> &) {
};
for_each(s, f2); // Fails to compile
}
/Users/ambarish> clang++ main.cxx
In file included from main.cxx:1:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/map:371:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/__tree:18:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/algorithm:793:9: error: no matching function for call to object of type '<lambda at main.cxx:24:15>'
__f(*__first);
^~~
/usr/local/include/boost/range/algorithm/for_each.hpp:80:12: note: in instantiation of function template specialization
'std::__1::for_each<std::__1::__map_iterator<std::__1::__tree_iterator<std::__1::pair<int, int>, std::__1::__tree_node<std::__1::pair<int, int>, void *> *, long> >, <lambda at main.cxx:24:15> >'
requested here
return std::for_each<
^
main.cxx:26:5: note: (skipping 1 context in backtrace; use -ftemplate-backtrace-limit=0 to see all)
for_each(s, f2);
^
main.cxx:24:15: note: candidate function not viable: no known conversion from 'value_type' (aka 'pair<__key_type, __mapped_type>') to 'pair<int, int> &' for 1st argument
auto f2 = [&](pair<int, int> &) {
^
1 error generated.
It doesn't compile because the value type of std::map<int, int> is not std::pair<int,int> but std::pair<const int, int>.
The reason first one (f1) compiles is because std::pair has got this constructor:
template< class U1, class U2 >
pair( pair<U1, U2>&& p );
and because f1 takes the argument by const reference. Now there's a suitable conversion which produces a temporary that can bind to a const reference easily.
Fix:
auto f2 = [&](pair<const int, int> &) { };
// or
auto f2 = [&](pair<int, int>) { };
The code appears to be using std::for_each in the boost library implementation, and C++11 style standard lambdas are being passed to the for_each function at any rate. It really depends on how the library passes the container's iterators to the std::for_each