The purpose of the following code is to display the contents of a structure. It is based on this answer.
#include <iostream>
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/range_c.hpp>
#include <boost/bind.hpp>
struct Node {
int a = 4;
double b = 2.2;
};
BOOST_FUSION_ADAPT_STRUCT(Node, b)
struct print_visitor {
template <class Index, class C> void operator()(Index, C &c) {
std::cout << boost::fusion::extension::struct_member_name<
C, Index::value>::call() << "="
<< boost::fusion::at<Index>(c) << std::endl;
}
};
template <class C> void print_fields(C &c) {
typedef boost::mpl::range_c<
int, 0, boost::fusion::result_of::size<C>::type::value> range;
boost::mpl::for_each<range>(
boost::bind<void>(print_visitor(), _1, boost::ref(c)));
}
int main() {
Node n;
print_fields(n);
}
The compiler (gcc version 4.8.2) complains:
invalid use of incomplete type ‘struct boost::fusion::result_of::at<Node, mpl_::integral_c<int, 0> >’
typedef typename T::type type;
What is the reason and how can I resolve this?
Here is the complete output of the compiler:
-*- mode: compilation; default-directory: "~/SearchLib/AstarWithPolicies/temp/" -*-
Compilation started at Thu Dec 10 11:54:27
make -k
g++ -Wall -Wextra -Werror -std=c++11 -pedantic -I ~/boost_1_59_0 -I /usr/include/cairomm-1.0/ -I /usr/include/cairo/ -I /usr/include/sigc++-2.0/ -I /usr/lib/x86_64-linux-gnu/sigc++-2.0/include/ -I /usr/include/freetype2/ -g -c temp.cpp
In file included from /home/meir/boost_1_59_0/boost/utility/enable_if.hpp:15:0,
from /home/meir/boost_1_59_0/boost/fusion/support/tag_of.hpp:11,
from /home/meir/boost_1_59_0/boost/fusion/support/category_of.hpp:12,
from /home/meir/boost_1_59_0/boost/fusion/adapted/struct/detail/extension.hpp:14,
from /home/meir/boost_1_59_0/boost/fusion/adapted/struct/adapt_struct.hpp:27,
from temp.cpp:7:
/home/meir/boost_1_59_0/boost/core/enable_if.hpp: In instantiation of ‘struct boost::lazy_disable_if_c<false, boost::fusion::result_of::at<Node, mpl_::integral_c<int, 0> > >’:
/home/meir/boost_1_59_0/boost/core/enable_if.hpp:70:10: required from ‘struct boost::lazy_disable_if<boost::is_const<Node>, boost::fusion::result_of::at<Node, mpl_::integral_c<int, 0> > >’
/home/meir/boost_1_59_0/boost/fusion/sequence/intrinsic_fwd.hpp:102:5: required by substitution of ‘template<class N, class Sequence> constexpr typename boost::lazy_disable_if<boost::is_const<Sequence>, boost::fusion::result_of::at<Sequence, N> >::type boost::fusion::at(Sequence&) [with N = mpl_::integral_c<int, 0>; Sequence = Node]’
temp.cpp:26:48: required from ‘void print_visitor::operator()(Index, C&) [with Index = mpl_::integral_c<int, 0>; C = Node]’
/home/meir/boost_1_59_0/boost/bind/bind.hpp:315:34: required from ‘void boost::_bi::list2<A1, A2>::operator()(boost::_bi::type<void>, F&, A&, int) [with F = print_visitor; A = boost::_bi::list1<mpl_::integral_c<int, 0>&>; A1 = boost::arg<1>; A2 = boost::reference_wrapper<Node>]’
/home/meir/boost_1_59_0/boost/bind/bind.hpp:907:50: required from ‘boost::_bi::bind_t<R, F, L>::result_type boost::_bi::bind_t<R, F, L>::operator()(A1&&) [with A1 = mpl_::integral_c<int, 0>&; R = void; F = print_visitor; L = boost::_bi::list2<boost::arg<1>, boost::reference_wrapper<Node> >; boost::_bi::bind_t<R, F, L>::result_type = void]’
/home/meir/boost_1_59_0/boost/mpl/for_each.hpp:78:25: required from ‘static void boost::mpl::aux::for_each_impl<false>::execute(Iterator*, LastIterator*, TransformFunc*, F) [with Iterator = boost::mpl::r_iter<mpl_::integral_c<int, 0> >; LastIterator = boost::mpl::r_iter<mpl_::integral_c<int, 1> >; TransformFunc = boost::mpl::identity<mpl_::na>; F = boost::_bi::bind_t<void, print_visitor, boost::_bi::list2<boost::arg<1>, boost::reference_wrapper<Node> > >]’
/home/meir/boost_1_59_0/boost/mpl/for_each.hpp:105:97: required from ‘void boost::mpl::for_each(F, Sequence*, TransformOp*) [with Sequence = boost::mpl::range_c<int, 0, 1>; TransformOp = boost::mpl::identity<mpl_::na>; F = boost::_bi::bind_t<void, print_visitor, boost::_bi::list2<boost::arg<1>, boost::reference_wrapper<Node> > >]’
/home/meir/boost_1_59_0/boost/mpl/for_each.hpp:118:48: required from ‘void boost::mpl::for_each(F, Sequence*) [with Sequence = boost::mpl::range_c<int, 0, 1>; F = boost::_bi::bind_t<void, print_visitor, boost::_bi::list2<boost::arg<1>, boost::reference_wrapper<Node> > >]’
temp.cpp:34:62: required from ‘void print_fields(C&) [with C = Node]’
temp.cpp:39:19: required from here
/home/meir/boost_1_59_0/boost/core/enable_if.hpp:63:30: error: invalid use of incomplete type ‘struct boost::fusion::result_of::at<Node, mpl_::integral_c<int, 0> >’
typedef typename T::type type;
^
In file included from /home/meir/boost_1_59_0/boost/fusion/sequence/intrinsic/begin.hpp:14:0,
from /home/meir/boost_1_59_0/boost/fusion/algorithm/iteration/detail/for_each.hpp:11,
from /home/meir/boost_1_59_0/boost/fusion/algorithm/iteration/for_each.hpp:12,
from /home/meir/boost_1_59_0/boost/fusion/include/for_each.hpp:11,
from temp.cpp:9:
/home/meir/boost_1_59_0/boost/fusion/sequence/intrinsic_fwd.hpp:53:16: error: declaration of ‘struct boost::fusion::result_of::at<Node, mpl_::integral_c<int, 0> >’
struct at;
^
temp.cpp: In instantiation of ‘void print_visitor::operator()(Index, C&) [with Index = mpl_::integral_c<int, 0>; C = Node]’:
/home/meir/boost_1_59_0/boost/bind/bind.hpp:315:34: required from ‘void boost::_bi::list2<A1, A2>::operator()(boost::_bi::type<void>, F&, A&, int) [with F = print_visitor; A = boost::_bi::list1<mpl_::integral_c<int, 0>&>; A1 = boost::arg<1>; A2 = boost::reference_wrapper<Node>]’
/home/meir/boost_1_59_0/boost/bind/bind.hpp:907:50: required from ‘boost::_bi::bind_t<R, F, L>::result_type boost::_bi::bind_t<R, F, L>::operator()(A1&&) [with A1 = mpl_::integral_c<int, 0>&; R = void; F = print_visitor; L = boost::_bi::list2<boost::arg<1>, boost::reference_wrapper<Node> >; boost::_bi::bind_t<R, F, L>::result_type = void]’
/home/meir/boost_1_59_0/boost/mpl/for_each.hpp:78:25: required from ‘static void boost::mpl::aux::for_each_impl<false>::execute(Iterator*, LastIterator*, TransformFunc*, F) [with Iterator = boost::mpl::r_iter<mpl_::integral_c<int, 0> >; LastIterator = boost::mpl::r_iter<mpl_::integral_c<int, 1> >; TransformFunc = boost::mpl::identity<mpl_::na>; F = boost::_bi::bind_t<void, print_visitor, boost::_bi::list2<boost::arg<1>, boost::reference_wrapper<Node> > >]’
/home/meir/boost_1_59_0/boost/mpl/for_each.hpp:105:97: required from ‘void boost::mpl::for_each(F, Sequence*, TransformOp*) [with Sequence = boost::mpl::range_c<int, 0, 1>; TransformOp = boost::mpl::identity<mpl_::na>; F = boost::_bi::bind_t<void, print_visitor, boost::_bi::list2<boost::arg<1>, boost::reference_wrapper<Node> > >]’
/home/meir/boost_1_59_0/boost/mpl/for_each.hpp:118:48: required from ‘void boost::mpl::for_each(F, Sequence*) [with Sequence = boost::mpl::range_c<int, 0, 1>; F = boost::_bi::bind_t<void, print_visitor, boost::_bi::list2<boost::arg<1>, boost::reference_wrapper<Node> > >]’
temp.cpp:34:62: required from ‘void print_fields(C&) [with C = Node]’
temp.cpp:39:19: required from here
temp.cpp:26:48: error: no matching function for call to ‘at(Node&)’
<< boost::fusion::at<Index>(c) << std::endl;
^
temp.cpp:26:48: note: candidates are:
In file included from /home/meir/boost_1_59_0/boost/fusion/sequence/intrinsic/begin.hpp:14:0,
from /home/meir/boost_1_59_0/boost/fusion/algorithm/iteration/detail/for_each.hpp:11,
from /home/meir/boost_1_59_0/boost/fusion/algorithm/iteration/for_each.hpp:12,
from /home/meir/boost_1_59_0/boost/fusion/include/for_each.hpp:11,
from temp.cpp:9:
/home/meir/boost_1_59_0/boost/fusion/sequence/intrinsic_fwd.hpp:102:5: note: template<class N, class Sequence> constexpr typename boost::lazy_disable_if<boost::is_const<Sequence>, boost::fusion::result_of::at<Sequence, N> >::type boost::fusion::at(Sequence&)
at(Sequence& seq);
^
/home/meir/boost_1_59_0/boost/fusion/sequence/intrinsic_fwd.hpp:102:5: note: substitution of deduced template arguments resulted in errors seen above
/home/meir/boost_1_59_0/boost/fusion/sequence/intrinsic_fwd.hpp:107:5: note: template<class N, class Sequence> constexpr typename boost::fusion::result_of::at<const Sequence, N>::type boost::fusion::at(const Sequence&)
at(Sequence const& seq);
^
/home/meir/boost_1_59_0/boost/fusion/sequence/intrinsic_fwd.hpp:107:5: note: template argument deduction/substitution failed:
/home/meir/boost_1_59_0/boost/fusion/sequence/intrinsic_fwd.hpp: In substitution of ‘template<class N, class Sequence> constexpr typename boost::fusion::result_of::at<const Sequence, N>::type boost::fusion::at(const Sequence&) [with N = mpl_::integral_c<int, 0>; Sequence = Node]’:
temp.cpp:26:48: required from ‘void print_visitor::operator()(Index, C&) [with Index = mpl_::integral_c<int, 0>; C = Node]’
/home/meir/boost_1_59_0/boost/bind/bind.hpp:315:34: required from ‘void boost::_bi::list2<A1, A2>::operator()(boost::_bi::type<void>, F&, A&, int) [with F = print_visitor; A = boost::_bi::list1<mpl_::integral_c<int, 0>&>; A1 = boost::arg<1>; A2 = boost::reference_wrapper<Node>]’
/home/meir/boost_1_59_0/boost/bind/bind.hpp:907:50: required from ‘boost::_bi::bind_t<R, F, L>::result_type boost::_bi::bind_t<R, F, L>::operator()(A1&&) [with A1 = mpl_::integral_c<int, 0>&; R = void; F = print_visitor; L = boost::_bi::list2<boost::arg<1>, boost::reference_wrapper<Node> >; boost::_bi::bind_t<R, F, L>::result_type = void]’
/home/meir/boost_1_59_0/boost/mpl/for_each.hpp:78:25: required from ‘static void boost::mpl::aux::for_each_impl<false>::execute(Iterator*, LastIterator*, TransformFunc*, F) [with Iterator = boost::mpl::r_iter<mpl_::integral_c<int, 0> >; LastIterator = boost::mpl::r_iter<mpl_::integral_c<int, 1> >; TransformFunc = boost::mpl::identity<mpl_::na>; F = boost::_bi::bind_t<void, print_visitor, boost::_bi::list2<boost::arg<1>, boost::reference_wrapper<Node> > >]’
/home/meir/boost_1_59_0/boost/mpl/for_each.hpp:105:97: required from ‘void boost::mpl::for_each(F, Sequence*, TransformOp*) [with Sequence = boost::mpl::range_c<int, 0, 1>; TransformOp = boost::mpl::identity<mpl_::na>; F = boost::_bi::bind_t<void, print_visitor, boost::_bi::list2<boost::arg<1>, boost::reference_wrapper<Node> > >]’
/home/meir/boost_1_59_0/boost/mpl/for_each.hpp:118:48: required from ‘void boost::mpl::for_each(F, Sequence*) [with Sequence = boost::mpl::range_c<int, 0, 1>; F = boost::_bi::bind_t<void, print_visitor, boost::_bi::list2<boost::arg<1>, boost::reference_wrapper<Node> > >]’
temp.cpp:34:62: required from ‘void print_fields(C&) [with C = Node]’
temp.cpp:39:19: required from here
/home/meir/boost_1_59_0/boost/fusion/sequence/intrinsic_fwd.hpp:107:5: error: invalid use of incomplete type ‘struct boost::fusion::result_of::at<const Node, mpl_::integral_c<int, 0> >’
/home/meir/boost_1_59_0/boost/fusion/sequence/intrinsic_fwd.hpp:53:16: error: declaration of ‘struct boost::fusion::result_of::at<const Node, mpl_::integral_c<int, 0> >’
struct at;
^
make: *** [all] Error 1
Compilation exited abnormally with code 2 at Thu Dec 10 11:54:27
I found this post as I'm practicing the use of the MPL. I got here as I wanted to print the struct_member_name which you were successful at. The short answer is to include <boost/fusion/sequence.hpp>. fusion::at requires a sequence
BOOST_FUSION_ADAPT_STRUCT does only that, it does not create a sequence for the target. If you had have accessed the value in the same way you access the name, it would have worked.
boost::fusion::extension::access::struct_member<C, Index::value>::template apply<C>::call(x)
returns the value of interest. But of course, there is always a better way, that much code is pretty ugly. Once you have a sequence applied to your class, everything gets easier. Now, I didn't find a for_each that iterates and passes types, fusion::for_each dereferences the value and passes that to the functor. So I took some code from boost\fusion\sequence\io\out.hpp which moves across an iterator range.
This example prints the data your way, the iterator way, and directly using fusion's IO. I'm sure I'm missing a whole lot, but this is my first day of actually writing compile time code that works. Thanks for your question, I may not have gotten so far without it!
#include <iostream>
#include <boost/mpl/range_c.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/fusion/sequence.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
//https://www.boost.org/doc/libs/1_78_0/libs/fusion/doc/html/fusion/support/deduce_sequence.html
#include <boost/bind/bind.hpp>
#include <boost/fusion/iterator/distance.hpp>
struct test {
int val;
std::string name;
};
BOOST_FUSION_ADAPT_STRUCT(test,val,name)
//the origanal with changes
struct print_class
{
template <class Index, typename T>
void operator()(Index, T& x) const
{
std::cout << "from print_class: " << boost::fusion::extension::struct_member_name<T, Index::value>::call()
<< " = " << boost::fusion::at<Index>(x) << std::endl;
}
};
using namespace boost::placeholders;
template <class C> void print_fields(C& c) {
typedef boost::mpl::range_c<
int, 0, boost::fusion::result_of::size<C>::type::value> range;
boost::mpl::for_each<range>(
boost::bind<void>(print_class(), _1, boost::ref(c)));
}
//I could not find anything like this in the fusion namespace, so...
//T is a fusion iterator
namespace boost::fusion {
template <typename T>
auto get_name(){return boost::fusion::extension::struct_member_name<T::seq_type, T::index::value>::call();
}
}
//shamelessly stolen from fusion/io....
namespace {
using namespace boost;
namespace bf = fusion;
struct print_sequence_loop
{
template <typename OS, typename First, typename Last>
static void call(OS&, First&, Last&, mpl::true_)
{
}
template <typename OS, typename First, typename Last>
static void call(OS& os, First& first, Last& last, mpl::false_)
{
bf::result_of::equal_to<
typename bf::result_of::next<First>::type
, Last
>is_last;
//The code that prints
os << bf::get_name<First>() << " = ";
os << *first << std::endl;
//End the code that prints
call(os, fusion::next(first), last, is_last);
}
template <typename OS, typename First, typename Last>
static void call(OS& os, First const& first, Last const& last)
{
bf::result_of::equal_to<First, Last> eq;
call(os, first, last, eq);
}
};
template <typename OS, typename Sequence>
inline void print_sequence(OS& os, Sequence& seq)
{
print_sequence_loop::call(os, fusion::begin(seq), fusion::end(seq));
}
}
using boost::fusion::operators::operator<<;
int main() {
test the_test{ 12,"this is me" };
print_sequence(std::cout, the_test);
std::cout << '\n';
print_fields(the_test);
std::cout << "\n\n this is using fusions OS\n";
std::cout << the_test << std::endl;
}
GCC demo And it works with MSVC.
Related
tl;dr
I'd like to understand what's wrong with the first code below, i.e. what the error is telling me.
MRE
I've been able to shorten the example to the following, which generates the same error as the original code below:
#include <boost/hana/functional/overload.hpp>
auto l1 = [](int){}; using L1 = decltype(l1);
auto l2 = [](double){}; using L2 = decltype(l2);
auto l3 = [](float){}; using L3 = decltype(l3);
using Ovl = boost::hana::overload_t<L1, L2, L3>
//const // uncomment to prevent error
;
Ovl ovl{l1,l2,l3};
Ovl ovl2{ovl}; // this triggers the compilation error
Original question
I'm having trouble understanding the errors I get when I try partially applying std::visit to a visitor function obtained via boost::hana::overload. (I know that I can't pass around the name of a template function, so I can't really partially apply std::visit, so I wrapped it in a generic lambda named just visit; I've not bothered doing perfect forwarding because I hardly believe it's relevant to the question.)
#include <boost/hana/functional/overload.hpp>
#include <boost/hana/functional/partial.hpp>
#include <boost/hana/functional/curry.hpp>
#include <iomanip>
#include <iostream>
#include <variant>
#include <vector>
using var_t = std::variant<int, long, double, std::string>;
int main() {
std::vector<var_t> vec = {10, 15l, 1.5, "hello"};
auto visitor = boost::hana::overload([](auto arg) { std::cout << arg << ' '; },
[](double arg) { std::cout << std::fixed << arg << ' '; },
[](const std::string& arg) { std::cout << std::quoted(arg) << ' '; }
);
auto visit = [](auto const& visitor_, auto const&... visited_){
return std::visit(visitor_, visited_...);
};
for (auto& v: vec) {
boost::hana::partial(visit, visitor)(v); // the error is essentially
boost::hana::curry<2>(visit)(visitor)(v); // the same on these 2 lines
}
std::cout << std::endl;
}
The error is (godbolt):
In file included from deleteme.cpp:1:
/usr/include/boost/hana/functional/overload.hpp: In instantiation of ‘constexpr boost::hana::overload_t<F, G>::overload_t(F_&&, G_&& ...) [with F_ = boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >&; G_ = {}; F = main()::<lambda(auto:22)>; G = {main()::<lambda(double)>, main()::<lambda(const string&)>}]’:
/usr/include/boost/hana/detail/ebo.hpp:62:36: required from ‘constexpr _hana::ebo<K, V, true>::ebo(T&&) [with T = boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >&; K = boost::hana::detail::bti<1>; V = boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >]’
/usr/include/boost/hana/basic_tuple.hpp:70:65: required from ‘constexpr boost::hana::detail::basic_tuple_impl<std::integer_sequence<long unsigned int, _Idx ...>, Xn ...>::basic_tuple_impl(Yn&& ...) [with Yn = {main()::<lambda(const auto:25&, const auto:26& ...)>, boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >&}; long unsigned int ...n = {0, 1}; Xn = {main()::<lambda(const auto:25&, const auto:26& ...)>, boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >}]’
/usr/include/boost/hana/basic_tuple.hpp:97:44: required from ‘constexpr boost::hana::basic_tuple<Xs>::basic_tuple(Yn&& ...) [with Yn = {main()::<lambda(const auto:25&, const auto:26& ...)>, boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >&}; Xn = {main()::<lambda(const auto:25&, const auto:26& ...)>, boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >}]’
/usr/include/boost/hana/functional/partial.hpp:71:15: required from ‘constexpr boost::hana::partial_t<std::integer_sequence<long unsigned int, _Idx ...>, F, X ...>::partial_t(boost::hana::make_partial_t::secret, T&& ...) [with T = {main()::<lambda(const auto:25&, const auto:26& ...)>, boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >&}; long unsigned int ...n = {0}; F = main()::<lambda(const auto:25&, const auto:26& ...)>; X = {boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >}]’
/usr/include/boost/hana/functional/partial.hpp:61:74: required from ‘constexpr boost::hana::partial_t<std::integer_sequence<long unsigned int, __integer_pack()(sizeof ... (X ...))...>, typename boost::hana::detail::decay<Xs>::type, typename boost::hana::detail::decay<X, typename std::remove_reference<X>::type>::type ...> boost::hana::make_partial_t::operator()(F&&, X&& ...) const [with F = main()::<lambda(const auto:25&, const auto:26& ...)>; X = {boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >&}; typename boost::hana::detail::decay<Xs>::type = main()::<lambda(const auto:25&, const auto:26& ...)>; typename std::remove_reference<_Tp>::type = main()::<lambda(const auto:25&, const auto:26& ...)>]’
/usr/include/boost/hana/functional/curry.hpp:149:24: required from ‘constexpr decltype(auto) boost::hana::curry_t<n, F>::operator()(X&& ...) && [with X = {boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >&}; long unsigned int n = 2; F = main()::<lambda(const auto:25&, const auto:26& ...)>]’
deleteme.cpp:31:37: required from here
/usr/include/boost/hana/functional/overload.hpp:53:61: error: no matching function for call to ‘boost::hana::overload_t<main()::<lambda(double)>, main()::<lambda(const string&)> >::overload_t()’
53 | , overload_t<G...>::type(static_cast<G_&&>(g)...)
| ^
/usr/include/boost/hana/functional/overload.hpp:51:28: note: candidate: ‘template<class F_, class ... G_> constexpr boost::hana::overload_t<F, G>::overload_t(F_&&, G_&& ...) [with F_ = F_; G_ = {G_ ...}; F = main()::<lambda(double)>; G = {main()::<lambda(const string&)>}]’
51 | constexpr explicit overload_t(F_&& f, G_&& ...g)
| ^~~~~~~~~~
/usr/include/boost/hana/functional/overload.hpp:51:28: note: template argument deduction/substitution failed:
/usr/include/boost/hana/functional/overload.hpp:53:61: note: candidate expects at least 1 argument, 0 provided
53 | , overload_t<G...>::type(static_cast<G_&&>(g)...)
| ^
/usr/include/boost/hana/functional/overload.hpp:42:12: note: candidate: ‘constexpr boost::hana::overload_t<main()::<lambda(double)>, main()::<lambda(const string&)> >::overload_t(const boost::hana::overload_t<main()::<lambda(double)>, main()::<lambda(const string&)> >&)’
42 | struct overload_t
| ^~~~~~~~~~
/usr/include/boost/hana/functional/overload.hpp:42:12: note: candidate expects 1 argument, 0 provided
/usr/include/boost/hana/functional/overload.hpp:42:12: note: candidate: ‘constexpr boost::hana::overload_t<main()::<lambda(double)>, main()::<lambda(const string&)> >::overload_t(boost::hana::overload_t<main()::<lambda(double)>, main()::<lambda(const string&)> >&&)’
/usr/include/boost/hana/functional/overload.hpp:42:12: note: candidate expects 1 argument, 0 provided
However, if I define visit to be "manually" curried, it behaves as I expect (godbolt):
#include <boost/hana/functional/overload.hpp>
#include <boost/hana/functional/partial.hpp>
#include <boost/hana/functional/curry.hpp>
#include <iomanip>
#include <iostream>
#include <variant>
#include <vector>
// the variant to visit
using var_t = std::variant<int, long, double, std::string>;
int main() {
std::vector<var_t> vec = {10, 15l, 1.5, "hello"};
auto visitor = boost::hana::overload([](auto arg) { std::cout << arg << ' '; },
[](double arg) { std::cout << std::fixed << arg << ' '; },
[](const std::string& arg) { std::cout << std::quoted(arg) << ' '; }
);
auto visit_c = [](auto const& visitor_){
return [&visitor_](auto const&... visited_){
return std::visit(visitor_, visited_...);
};
};
for (auto& v: vec) {
visit_c(visitor)(v);
}
std::cout << std::endl;
}
(Example adapted from cppreference.)
On top of the above MRE, I've verified that these assertions all pass,
static_assert(std::is_base_of_v<L1, Ovl>);
static_assert(std::is_base_of_v<boost::hana::overload_t<L2, L3>::type, Ovl>);
static_assert(std::is_base_of_v<boost::hana::overload_t<L2, L3>, Ovl>);
static_assert(std::is_base_of_v<L2, boost::hana::overload_t<L2, L3>::type>);
static_assert(std::is_base_of_v<L3, boost::hana::overload_t<L2, L3>::type>);
which, given how boost::hana::ovearload_t is written, means that the instantiation of boost::hana::overload_t<L1, L2, L3> has this form (not valid C++, here I play the role of the compiler):
struct overload_t<L1, L2, L3>
: L1
, overload_t<L2, L3>
{
using type = overload_t;
using L1::operator();
using overload_t<L2, L3>::operator();
template <typename F_, typename ...G_>
constexpr explicit overload_t(F_&& f, G_&& ...g)
: L1(static_cast<F_&&>(f))
, overload_t<L2, L3>(static_cast<G_&&>(g)...) // error points here
{ }
};
Therefore, when the compiler sees Ovl ovl2{ovl};, it has to construct ovl2 from ovl; to do so, there are two possibilities:
instantiating template <typename F_, typename ...G_> constexpr explicit overload_t<L1, L2, L3>::overload_t(F_&& f, G_&& ...g) with F_ = overload_t<L1, L2, L3>& and an empty pack G_..., which results in a constructor with signature overload_t<L1, L2, L3>::overload_t(overload_t<L1, L2, L3>&) which is good for copy a non-const lvalue (such as ovl);
using the copy constructor, the implicit definition of which is not prevented by the user-defined template constructor (see Effective Modern C++ by Scott Meyers, page 115, Item 17).
The implicitly defined copy constructor, however, has signature overload_t<L1, L2, L3>::overload_t(overload_t<L1, L2, L3> const&), which is not as good a match as overload_t<L1, L2, L3>::overload_t(overload_t<L1, L2, L3>&) when they are fed with the non-const lvalue ovl, so the compliler will have to choose the first alternative above, which will in turn make it try the construction overload_t<L2, L3>(), which maps nicely to the error:
error: no matching function for call to
‘boost::hana::overload_t<<lambda(double)>, <lambda(float)> >::overload_t()’
Making ovl const solves the problem because it makes the implicitly defined copy constructor, overload_t<L1, L2, L3>::overload_t(overload_t<L1, L2, L3> const&) an exact match; the template constructor too is an exact match when instantiated with F_ = overload_t<L1, L2, L3> const&, but the former is preferred because it is not templated.
std::moveing ovl too solves the problem, for the same reason: the implicitly defined copy constructor is an exact match for copying an rvalue, and it is preferred to the likewise exact match of the templated constructor.
I want to accumulate complex numbers in boost.compute library, but I face compile error when I run the below code.
I am using boost 1.65.0
P.S: the code is incomplete. I just summarize it to show the compile error.
#include <boost/compute/core.hpp>
#include <boost/compute/platform.hpp>
#include <boost/compute/algorithm.hpp>
#include <boost/compute/types/complex.hpp>
#include <complex>
namespace compute = boost::compute;
int main()
{
compute::vector<std::complex<double_t>> v;
compute::accumulate(v.begin(),v.end(),std::complex<double_t>(0,0));
}
and compiler output is:
In file included from ~/boost/compute/memory_object.hpp:16:0,
from ~/boost/compute/buffer.hpp:17,
from ~/boost/compute/core.hpp:18,
from ~/main.cpp:1:
~/boost/compute/kernel.hpp: In instantiation of ‘struct boost::compute::detail::set_kernel_arg<std::complex<double> >’:
~/boost/compute/kernel.hpp:251:17: required from ‘void boost::compute::kernel::set_arg(size_t, const T&) [with T = std::complex<double>; size_t = long unsigned int]’
~/boost/compute/algorithm/detail/serial_accumulate.hpp:46:5: required from ‘void boost::compute::detail::serial_accumulate(InputIterator, InputIterator, OutputIterator, T, BinaryFunction, boost::compute::command_queue&) [with InputIterator = boost::compute::buffer_iterator<std::complex<double> >; OutputIterator = boost::compute::buffer_iterator<std::complex<double> >; T = std::complex<double>; BinaryFunction = boost::compute::plus<std::complex<double> >]’
~/boost/compute/algorithm/accumulate.hpp:46:30: required from ‘T boost::compute::detail::generic_accumulate(InputIterator, InputIterator, T, BinaryFunction, boost::compute::command_queue&) [with InputIterator = boost::compute::buffer_iterator<std::complex<double> >; T = std::complex<double>; BinaryFunction = boost::compute::plus<std::complex<double> >]’
~/boost/compute/algorithm/accumulate.hpp:118:34: required from ‘T boost::compute::detail::dispatch_accumulate(InputIterator, InputIterator, T, BinaryFunction, boost::compute::command_queue&) [with InputIterator = boost::compute::buffer_iterator<std::complex<double> >; T = std::complex<double>; BinaryFunction = boost::compute::plus<std::complex<double> >]’
~/boost/compute/algorithm/accumulate.hpp:182:39: required from ‘T boost::compute::accumulate(InputIterator, InputIterator, T, boost::compute::command_queue&) [with InputIterator = boost::compute::buffer_iterator<std::complex<double> >; T = std::complex<double>]’
~/main.cpp:22:70: required from here
~/boost/compute/kernel.hpp:398:5: error: no type named ‘type’ in ‘struct boost::enable_if<boost::compute::is_fundamental<std::complex<double> >, void>’
operator()(kernel &kernel_, size_t index, const T &value)
^~~~~~~~
In file included from ~/boost/compute/memory_object.hpp:16:0,
from ~/boost/compute/buffer.hpp:17,
from ~/boost/compute/core.hpp:18,
from ~/main.cpp:1:
~/boost/compute/kernel.hpp: In instantiation of ‘void boost::compute::kernel::set_arg(size_t, const T&) [with T = std::complex<double>; size_t = long unsigned int]’:
~/boost/compute/algorithm/detail/serial_accumulate.hpp:46:5: required from ‘void boost::compute::detail::serial_accumulate(InputIterator, InputIterator, OutputIterator, T, BinaryFunction, boost::compute::command_queue&) [with InputIterator = boost::compute::buffer_iterator<std::complex<double> >; OutputIterator = boost::compute::buffer_iterator<std::complex<double> >; T = std::complex<double>; BinaryFunction = boost::compute::plus<std::complex<double> >]’
~/boost/compute/algorithm/accumulate.hpp:46:30: required from ‘T boost::compute::detail::generic_accumulate(InputIterator, InputIterator, T, BinaryFunction, boost::compute::command_queue&) [with InputIterator = boost::compute::buffer_iterator<std::complex<double> >; T = std::complex<double>; BinaryFunction = boost::compute::plus<std::complex<double> >]’
~/boost/compute/algorithm/accumulate.hpp:118:34: required from ‘T boost::compute::detail::dispatch_accumulate(InputIterator, InputIterator, T, BinaryFunction, boost::compute::command_queue&) [with InputIterator = boost::compute::buffer_iterator<std::complex<double> >; T = std::complex<double>; BinaryFunction = boost::compute::plus<std::complex<double> >]’
~/boost/compute/algorithm/accumulate.hpp:182:39: required from ‘T boost::compute::accumulate(InputIterator, InputIterator, T, boost::compute::command_queue&) [with InputIterator = boost::compute::buffer_iterator<std::complex<double> >; T = std::complex<double>]’
~/main.cpp:22:70: required from here
~/boost/compute/kernel.hpp:251:36: error: no match for call to ‘(boost::compute::detail::set_kernel_arg<std::complex<double> >) (boost::compute::kernel&, size_t&, const std::complex<double>&)’
detail::set_kernel_arg<T>()(*this, index, value);
~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
Demo
foo_allocator is a working allocator for stl containers. It wraps over a base allocator type and forwards allocate(), deallocate(), operator==, operator!=, etc to the base.
#include <iostream>
#include <string>
#include <vector>
#include <memory>
template <typename T>
class bar_allocator : public std::allocator<T> {};
template <typename T, typename base=bar_allocator<T>>
class foo_allocator {
public:
typedef typename std::allocator_traits<base>::value_type value_type;
template <typename U, typename A> friend class foo_allocator;
template<class U>
struct rebind {
typedef foo_allocator<U,
typename std::allocator_traits<base>::template rebind_alloc<U>> other;
};
// Construct a dummy allocator from another dummy allocator with the same base_allocator but with different type.
template <typename U>
foo_allocator(
const foo_allocator<U,
typename std::allocator_traits<base>::template rebind_alloc<U>>& other) noexcept :
alloc(other.alloc) {}
foo_allocator() = default;
template<typename... Args>
foo_allocator(Args &&... args) requires (std::is_constructible_v<base, Args...>) : alloc(std::forward<Args>(args)...) {}
T* allocate(std::size_t n) {
T* p = alloc.allocate(n);
return p;
}
void deallocate(T* p, std::size_t size) noexcept {
alloc.deallocate(p, size);
}
private:
base alloc;
};
template <typename T, typename U, typename base_allocator>
inline bool operator == (const foo_allocator<T, base_allocator>& a,
const foo_allocator<U, typename std::allocator_traits<base_allocator>::template rebind_alloc<U>>& b)
{
return a.alloc == b.alloc;
}
template <typename T, typename U, typename base_allocator>
inline bool operator != (const foo_allocator<T, base_allocator>& a,
const foo_allocator<U, typename std::allocator_traits<base_allocator>::template rebind_alloc<U>>& b)
{
return a.alloc != b.alloc;
}
int main()
{
// Works fine
std::vector<int, foo_allocator<int>> v;
for (int i = 0; i < 100; i++)
v.push_back(i);
// Breaks!
// foo_allocator<int> foo;
// std::shared_ptr<int> ptr2 = std::allocate_shared<int>(foo);
}
However, it doesn't work with allocate_shared. If you try to allocate_shared with this allocator, you get a compiler error of the following:
In file included from /usr/local/include/c++/9.2.0/bits/shared_ptr.h:52,
from /usr/local/include/c++/9.2.0/memory:81,
from main.cpp:4:
/usr/local/include/c++/9.2.0/bits/shared_ptr_base.h: In instantiation of 'std::__shared_count<_Lp>::__shared_count(_Tp*&, std::_Sp_alloc_shared_tag<_Alloc>, _Args&& ...) [with _Tp = int; _Alloc = foo_allocator<int>; _Args = {}; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]':
/usr/local/include/c++/9.2.0/bits/shared_ptr_base.h:1344:71: required from 'std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = foo_allocator<int>; _Args = {}; _Tp = int; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]'
/usr/local/include/c++/9.2.0/bits/shared_ptr.h:359:59: required from 'std::shared_ptr<_Tp>::shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = foo_allocator<int>; _Args = {}; _Tp = int]'
/usr/local/include/c++/9.2.0/bits/shared_ptr.h:701:14: required from 'std::shared_ptr<_Tp> std::allocate_shared(const _Alloc&, _Args&& ...) [with _Tp = int; _Alloc = foo_allocator<int>; _Args = {}]'
main.cpp:71:62: required from here
/usr/local/include/c++/9.2.0/bits/shared_ptr_base.h:676:43: error: no matching function for call to 'foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >::foo_allocator(const foo_allocator<int>&)'
676 | typename _Sp_cp_type::__allocator_type __a2(__a._M_a);
| ^~~~
main.cpp:32:5: note: candidate: 'foo_allocator<T, base>::foo_allocator(Args&& ...) requires is_constructible_v<base, Args ...> [with Args = {const foo_allocator<int, bar_allocator<int> >&}; T = std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>; base = std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> >]'
32 | foo_allocator(Args &&... args) requires (std::is_constructible_v<base, Args...>) : alloc(std::forward<Args>(args)...) {}
| ^~~~~~~~~~~~~
main.cpp:32:5: note: constraints not satisfied
main.cpp:32:5: note: 'is_constructible_v<base, Args ...>' evaluated to false
main.cpp:29:5: note: candidate: 'constexpr foo_allocator<T, base>::foo_allocator() [with T = std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>; base = std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> >]'
29 | foo_allocator() = default;
| ^~~~~~~~~~~~~
main.cpp:29:5: note: candidate expects 0 arguments, 1 provided
main.cpp:24:5: note: candidate: 'template<class U> foo_allocator<T, base>::foo_allocator(const foo_allocator<U, typename std::allocator_traits<_Alloc>::rebind_alloc<U> >&)'
24 | foo_allocator(
| ^~~~~~~~~~~~~
main.cpp:24:5: note: template argument deduction/substitution failed:
In file included from /usr/local/include/c++/9.2.0/bits/shared_ptr.h:52,
from /usr/local/include/c++/9.2.0/memory:81,
from main.cpp:4:
/usr/local/include/c++/9.2.0/bits/shared_ptr_base.h:676:43: note: mismatched types 'std::allocator<_CharT>' and 'bar_allocator<int>'
676 | typename _Sp_cp_type::__allocator_type __a2(__a._M_a);
| ^~~~
main.cpp:10:7: note: candidate: 'constexpr foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >::foo_allocator(const foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >&)'
10 | class foo_allocator {
| ^~~~~~~~~~~~~
main.cpp:10:7: note: no known conversion for argument 1 from 'const foo_allocator<int>' to 'const foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >&'
main.cpp:10:7: note: candidate: 'constexpr foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >::foo_allocator(foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >&&)'
main.cpp:10:7: note: no known conversion for argument 1 from 'const foo_allocator<int>' to 'foo_allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, std::allocator<std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic> > >&&'
What I can gather from the error messages
std::shared_ptr does some rebinding to create a struct that wraps over the int we're trying to allocate. In particular, it tries to allocate the type std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>, which I'm guessing has the int but also other fields used to make sure shared_ptrs are threadsafe.
As a result, we have this really ugly struct that does some self-referencing with foo_allocator:
foo_allocator<
std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>,
std::allocator<
std::_Sp_counted_ptr_inplace<int, foo_allocator<int>, __gnu_cxx::_S_atomic>
>
>
But wait! Why is std::allocator<> here? We never specified std::allocator<> as our base allocator! It should've been bar_allocator!
Later down the error log, we see the following bizarre line:
mismatched types 'std::allocator<_CharT>' and 'bar_allocator<int>'
Other than the std::allocator<> showing up again, where did _CharT come from? doesn't this template type usually show up in strings?
Any help would be greatly appreciated. I've scratched my head about this for a while and couldn't come up with any reasonable fixes.
Thanks to #Nicol Bolas and #Igor Tandetnik, I was able to figure out the reason. As they said, by inheriting an allocator you also inherit the rebind struct which is actually rebinding for the base class. This isn't what we want (allocators I guess don't work well with inheritance), so we'd have to add the following to get this to work:
template <typename T>
class bar_allocator : public std::allocator<T> {
public:
bar_allocator() = default;
template <typename U>
bar_allocator(const bar_allocator<U>& other) : std::allocator<T>(other){
}
template<class U>
struct rebind {
typedef bar_allocator<U> other;
};
};
Demo
For some context, I'm writing an allocator that takes in a base allocator as a template type, and does nothing but forward the allocate() and deallocate() calls to the underlying allocator member. Creating std::vectors with this custom allocator works fine. I tried writing a make_shared wrapper that will use dummy_allocator<T, std::allocator<T>> by default but it wasn't successful. Here is a reproducible example:
#include <memory>
namespace test {
template<typename T, typename base_allocator=std::allocator<T>>
class dummy_allocator {
public:
typedef typename std::allocator_traits<base_allocator>::size_type size_type;
typedef typename std::allocator_traits<base_allocator>::difference_type difference_type;
typedef typename std::allocator_traits<base_allocator>::pointer pointer;
typedef typename std::allocator_traits<base_allocator>::const_pointer const_pointer;
typedef typename std::allocator_traits<base_allocator>::value_type value_type;
template<class U>
struct rebind {
typedef dummy_allocator<U,
typename std::allocator_traits<base_allocator>::template rebind_alloc<U>> other;
};
template<typename... Args>
dummy_allocator(Args &&... args) noexcept : alloc(std::forward<Args>(args)...) {}
dummy_allocator(const dummy_allocator& a) = default;
[[nodiscard]] T *allocate(std::size_t n) {
T *p = alloc.allocate(n);
return p;
}
void deallocate(T *p, std::size_t size) noexcept {
alloc.deallocate(p, size);
}
private:
base_allocator alloc;
};
/// Allocate using a wrapped version of passed in allocator
template <typename T, typename Alloc, typename... Args>
std::shared_ptr<T> allocate_shared(const Alloc& alloc, Args&&... args) {
auto dummy_alloc = dummy_allocator<T, Alloc>(alloc);
return std::allocate_shared<T>(dummy_alloc, std::forward<Args>(args)...);
}
/// Create a shared pointer from a default stl allocator wrapped in profile allocator.
template <typename T, typename... Args>
std::shared_ptr<T> make_shared(Args&&... args) {
return test::allocate_shared<T>(std::allocator<T>(), std::forward<Args>(args)...);
}
} // namespace test
int main() {
auto ptr = test::make_shared<double>();
return 0;
}
When I ran the above code, the compiler generated some bizarre template substitution failure errors:
/usr/include/c++/10.1.0/bits/shared_ptr_base.h:679:43: required from ‘std::__shared_count<_Lp>::__shared_count(_Tp*&, std::_Sp_alloc_shared_tag<_Alloc>, _Args&& ...) [with _Tp = double; _Alloc = test::dummy_allocator<double, std::allocator<double> >; _Args = {}; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]’
/usr/include/c++/10.1.0/bits/shared_ptr_base.h:1371:71: required from ‘std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = test::dummy_allocator<double, std::allocator<double> >; _Args = {}; _Tp = double; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]’
/usr/include/c++/10.1.0/bits/shared_ptr.h:408:59: required from ‘std::shared_ptr<_Tp>::shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = test::dummy_allocator<double, std::allocator<double> >; _Args = {}; _Tp = double]’
/usr/include/c++/10.1.0/bits/shared_ptr.h:859:14: required from ‘std::shared_ptr<_Tp> std::allocate_shared(const _Alloc&, _Args&& ...) [with _Tp = double; _Alloc = test::dummy_allocator<double, std::allocator<double> >; _Args = {}]’
/home/ray/home/testing/src/alloc.cpp:44:35: required from ‘std::shared_ptr<_Tp> test::allocate_shared(const Alloc&, Args&& ...) [with T = double; Alloc = std::allocator<double>; Args = {}]’
/home/ray/home/testing/src/alloc.cpp:50:36: required from ‘std::shared_ptr<_Tp> test::make_shared(Args&& ...) [with T = double; Args = {}]’
/home/ray/home/testing/src/alloc.cpp:86:46: required from here
/home/ray/home/testing/src/alloc.cpp:25:82: error: no matching function for call to ‘std::allocator<std::_Sp_counted_ptr_inplace<double, test::dummy_allocator<double, std::allocator<double> >, __gnu_cxx::_S_atomic> >::allocator(const test::dummy_allocator<double, std::allocator<double> >&)’
25 | dummy_allocator(Args &&... args) noexcept : alloc(std::forward<Args>(args)...) {}
| ^
In file included from /usr/include/c++/10.1.0/list:61,
from /home/ray/home/testing/src/alloc.cpp:3:
/usr/include/c++/10.1.0/bits/allocator.h:157:2: note: candidate: ‘template<class _Tp1> constexpr std::allocator< <template-parameter-1-1> >::allocator(const std::allocator<_Tp1>&) [with _Tp1 = _Tp1; _Tp = std::_Sp_counted_ptr_inplace<double, test::dummy_allocator<double, std::allocator<double> >, __gnu_cxx::_S_atomic>]’
157 | allocator(const allocator<_Tp1>&) _GLIBCXX_NOTHROW { }
| ^~~~~~~~~
/usr/include/c++/10.1.0/bits/allocator.h:157:2: note: template argument deduction/substitution failed:
/home/ray/home/testing/src/alloc.cpp:25:82: note: ‘const test::dummy_allocator<double, std::allocator<double> >’ is not derived from ‘const std::allocator<_Up>’
25 | dummy_allocator(Args &&... args) noexcept : alloc(std::forward<Args>(args)...) {}
| ^
In file included from /usr/include/c++/10.1.0/list:61,
from /home/ray/home/testing/src/alloc.cpp:3:
/usr/include/c++/10.1.0/bits/allocator.h:147:7: note: candidate: ‘constexpr std::allocator< <template-parameter-1-1> >::allocator(const std::allocator< <template-parameter-1-1> >&) [with _Tp = std::_Sp_counted_ptr_inplace<double, test::dummy_allocator<double, std::allocator<double> >, __gnu_cxx::_S_atomic>]’
147 | allocator(const allocator& __a) _GLIBCXX_NOTHROW
| ^~~~~~~~~
/usr/include/c++/10.1.0/bits/allocator.h:147:34: note: no known conversion for argument 1 from ‘const test::dummy_allocator<double, std::allocator<double> >’ to ‘const std::allocator<std::_Sp_counted_ptr_inplace<double, test::dummy_allocator<double, std::allocator<double> >, __gnu_cxx::_S_atomic> >&’
147 | allocator(const allocator& __a) _GLIBCXX_NOTHROW
| ~~~~~~~~~~~~~~~~~^~~
/usr/include/c++/10.1.0/bits/allocator.h:144:7: note: candidate: ‘constexpr std::allocator< <template-parameter-1-1> >::allocator() [with _Tp = std::_Sp_counted_ptr_inplace<double, test::dummy_allocator<double, std::allocator<double> >, __gnu_cxx::_S_atomic>]’
144 | allocator() _GLIBCXX_NOTHROW { }
| ^~~~~~~~~
/usr/include/c++/10.1.0/bits/allocator.h:144:7: note: candidate expects 0 arguments, 1 provided
... (The above error basically is repeated 2 more times)
In particular, this error seemed representative of the problem occurring:
error: no matching function for call to ‘std::allocator<std::_Sp_counted_ptr_inplace<double, test::dummy_allocator<double, std::allocator<double> >, __gnu_cxx::_S_atomic> >::allocator(const test::dummy_allocator<double, std::allocator<double> >&)’
Where it basically says "in the constructor of dummy_allocator, you can't pass in a dummy_allocator to the std::allocator's constructor". But I'm not doing that. In allocate_shared, I'm passing in the std::allocator into the dummy_allocator.
I've really scratched my head reading the compiler error but came to no conclusions on what I'm doing wrong. Any help would be greatly appreciated!
EDIT: I think I have a hunch that whatever magic shared_ptr is doing underneath, it's trying to do a copy constructor of my dummy_allocator, and perfect forwarding is capturing the copy construction instead an actual copy constructor. However, I have no idea how to solve this as it's a variadic template and I can't use std::is_same<Args, dummy_allocator> as a requires clause in the perfect forwarding constructor.
So as Daniel Langr has pointed out, just copy constructing the dummy_allocator will fail. I fixed that issue with the following requires clause:
...
template <typename T1, typename ...TV>
struct is_dummy : std::is_same<typename std::decay<T1>::type, dummy_allocator<T, base_allocator>>{
};
template<typename... Args>
requires (!is_dummy<Args...>::value)
dummy_allocator(Args &&... args) noexcept : alloc(std::forward<Args>(args)...) {}
dummy_allocator() = default;
dummy_allocator(const dummy_allocator& a) = default;
...
However, that's not enough to fix the shared_ptr issue, where the error is a bit longer...*
So here is the error you get:
#include <memory>
namespace test {
template<typename T, typename base_allocator=std::allocator<T>>
class dummy_allocator {
public:
/// Necessary for allocators, propagate exactly what the base_allocator
/// wants.
typedef typename std::allocator_traits<base_allocator>::size_type size_type;
typedef typename std::allocator_traits<base_allocator>::difference_type difference_type;
typedef typename std::allocator_traits<base_allocator>::pointer pointer;
typedef typename std::allocator_traits<base_allocator>::const_pointer const_pointer;
typedef typename std::allocator_traits<base_allocator>::value_type value_type;
template<class U>
struct rebind {
typedef dummy_allocator<U,
typename std::allocator_traits<base_allocator>::template rebind_alloc<U>> other;
};
template <typename T1, typename ...TV>
struct is_dummy : std::is_same<typename std::decay<T1>::type, dummy_allocator<T, base_allocator>>{
};
template<typename... Args>
requires (!is_dummy<Args...>::value)
dummy_allocator(Args &&... args) noexcept : alloc(std::forward<Args>(args)...) {}
dummy_allocator() = default;
dummy_allocator(const dummy_allocator& a) = default;
[[nodiscard]] T *allocate(std::size_t n) {
T *p = alloc.allocate(n);
return p;
}
void deallocate(T *p, std::size_t size) noexcept {
alloc.deallocate(p, size);
}
private:
base_allocator alloc;
};
/// Allocate using a wrapped version of passed in allocator
template <typename T, typename Alloc, typename... Args>
auto allocate_shared(const Alloc& alloc, Args&&... args) {
auto dummy_alloc = dummy_allocator<T, Alloc>(alloc);
return std::allocate_shared<T>(dummy_alloc, std::forward<Args>(args)...);
}
/// Create a shared pointer from a default stl allocator wrapped in profile allocator.
template <typename T, typename... Args>
auto make_shared(Args&&... args) {
return test::allocate_shared<T>(std::allocator<T>(), std::forward<Args>(args)...);
}
} // namespace test
int main() {
// This will fail
auto ptr = test::make_shared<double>();
// This will now work
auto dummy_alloc = test::dummy_allocator<int, std::allocator<int>>();
auto dummy_alloc2 = test::dummy_allocator<int, std::allocator<int>>(dummy_alloc);
return 0;
}
The associated error:
/usr/include/c++/10.1.0/bits/shared_ptr_base.h:679:43: required from ‘std::__shared_count<_Lp>::__shared_count(_Tp*&, std::_Sp_alloc_shared_tag<_Alloc>, _Args&& ...) [with _Tp = double; _Alloc = test::dummy_allocator<double, std::allocator<double> >; _Args = {}; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]’
/usr/include/c++/10.1.0/bits/shared_ptr_base.h:1371:71: required from ‘std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = test::dummy_allocator<double, std::allocator<double> >; _Args = {}; _Tp = double; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]’
/usr/include/c++/10.1.0/bits/shared_ptr.h:408:59: required from ‘std::shared_ptr<_Tp>::shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = test::dummy_allocator<double, std::allocator<double> >; _Args = {}; _Tp = double]’
/usr/include/c++/10.1.0/bits/shared_ptr.h:859:14: required from ‘std::shared_ptr<_Tp> std::allocate_shared(const _Alloc&, _Args&& ...) [with _Tp = double; _Alloc = test::dummy_allocator<double, std::allocator<double> >; _Args = {}]’
/home/ray/home/test/src/alloc.cpp:53:35: required from ‘auto test::allocate_shared(const Alloc&, Args&& ...) [with T = double; Alloc = std::allocator<double>; Args = {}]’
/home/ray/home/test/src/alloc.cpp:59:36: required from ‘auto test::make_shared(Args&& ...) [with T = double; Args = {}]’
/home/ray/home/test/src/alloc.cpp:95:46: required from here
/home/ray/home/test/src/alloc.cpp:31:82: error: no matching function for call to ‘std::allocator<std::_Sp_counted_ptr_inplace<double, test::dummy_allocator<double, std::allocator<double> >, __gnu_cxx::_S_atomic> >::allocator(const test::dummy_allocator<double, std::allocator<double> >&)’
31 | dummy_allocator(Args &&... args) noexcept : alloc(std::forward<Args>(args)...) {}
| ^
In file included from /usr/include/c++/10.1.0/list:61,
from /home/ray/home/test/src/alloc.cpp:3:
/usr/include/c++/10.1.0/bits/allocator.h:157:2: note: candidate: ‘template<class _Tp1> constexpr std::allocator< <template-parameter-1-1> >::allocator(const std::allocator<_Tp1>&) [with _Tp1 = _Tp1; _Tp = std::_Sp_counted_ptr_inplace<double, test::dummy_allocator<double, std::allocator<double> >, __gnu_cxx::_S_atomic>]’
157 | allocator(const allocator<_Tp1>&) _GLIBCXX_NOTHROW { }
| ^~~~~~~~~
/usr/include/c++/10.1.0/bits/allocator.h:157:2: note: template argument deduction/substitution failed:
/home/ray/home/test/src/alloc.cpp:31:82: note: ‘const test::dummy_allocator<double, std::allocator<double> >’ is not derived from ‘const std::allocator<_Up>’
31 | dummy_allocator(Args &&... args) noexcept : alloc(std::forward<Args>(args)...) {}
| ^
In file included from /usr/include/c++/10.1.0/list:61,
from /home/ray/home/test/src/alloc.cpp:3:
/usr/include/c++/10.1.0/bits/allocator.h:147:7: note: candidate: ‘constexpr std::allocator< <template-parameter-1-1> >::allocator(const std::allocator< <template-parameter-1-1> >&) [with _Tp = std::_Sp_counted_ptr_inplace<double, test::dummy_allocator<double, std::allocator<double> >, __gnu_cxx::_S_atomic>]’
147 | allocator(const allocator& __a) _GLIBCXX_NOTHROW
| ^~~~~~~~~
/usr/include/c++/10.1.0/bits/allocator.h:147:34: note: no known conversion for argument 1 from ‘const test::dummy_allocator<double, std::allocator<double> >’ to ‘const std::allocator<std::_Sp_counted_ptr_inplace<double, test::dummy_allocator<double, std::allocator<double> >, __gnu_cxx::_S_atomic> >&’
147 | allocator(const allocator& __a) _GLIBCXX_NOTHROW
| ~~~~~~~~~~~~~~~~~^~~
/usr/include/c++/10.1.0/bits/allocator.h:144:7: note: candidate: ‘constexpr std::allocator< <template-parameter-1-1> >::allocator() [with _Tp = std::_Sp_counted_ptr_inplace<double, test::dummy_allocator<double, std::allocator<double> >, __gnu_cxx::_S_atomic>]’
144 | allocator() _GLIBCXX_NOTHROW { }
| ^~~~~~~~~
/usr/include/c++/10.1.0/bits/allocator.h:144:7: note: candidate expects 0 arguments, 1 provided
Just remove the variadic constructor and add these two:
dummy_allocator(const base_allocator& a) : alloc(a)
{}
template<class U, class Alloc>
dummy_allocator(const dummy_allocator<U, Alloc>& a) : alloc(a.alloc)
{}
Demo
This doesn't solve the issue if I wanted to forward arguments to an allocator that requires arguments :(
Then you can use std::is_constructible:
template<class... Args>
dummy_allocator(Args&&... args)
requires(std::is_constructible_v<base_allocator, Args...>)
: alloc(std::forward<Args>(args)...)
{}
template<class U, class Alloc>
dummy_allocator(const dummy_allocator<U, Alloc>& a) : alloc(a.alloc)
{}
Demo
Note that template<class U, class Alloc> dummy_allocator constuctor is needed in both cases, because dummy_allocator a(b) should be well-formed for any b of rebinded allocator type.
I think your hunch is correct. One always has to be careful with perfect forwarding constructors and the normal copy constructor. I think there are multiple solutions but I haven't tested any of them yet.
First you could provide a better match than the perfect forwarding constructor by adding dummy_allocator(dummy_allocator&) forwarding to the normal copy constructor. The perfect forwarding constructor matches better than the copy constructor, when the passed dummy_allocator is not const and hence the copy constructor has to do const conversion. But this would require also overloading the move constructor (I think) since otherwise you get the same error when someone somewhere tries to move the dummy_allocator.
A bit more tricky but probably safer is doing the following:
template<class... Args>
dummy_allocator(Args&&... args) requires (sizeof...(Args) != 1) : alloc(std::forward<Args>(args)...) {}
template<class Arg>
dummy_allocator(Arg&& arg) requires (!std::is_same_v<std::decay_t<Arg>, dummy_allocator>) : alloc(std::forward<Arg>(arg)) {}
dummy_allocator(const dummy_allocator&) = default;
I hope I got the requires clauses right, I haven't worked much with that yet. But doing SFINAE in constructors is a royal pain in the ass.
#Evg's answer is correct in the implementation for making the arguments constructible, but it did not yet solve the problem of why I was getting the shared_ptr issue. I solved using his implementation because I felt like using is_constructible::value is the best way to approach it, but we need to add a constructor that allows for different types to rebind:
template<typename T, typename base_allocator=std::allocator<T>>
class dummy_allocator {
public:
/// Necessary for allocators, propagate exactly what the base_allocator
/// wants.
typedef typename std::allocator_traits<base_allocator>::size_type size_type;
typedef typename std::allocator_traits<base_allocator>::difference_type difference_type;
typedef typename std::allocator_traits<base_allocator>::pointer pointer;
typedef typename std::allocator_traits<base_allocator>::const_pointer const_pointer;
typedef typename std::allocator_traits<base_allocator>::value_type value_type;
template<class U>
struct rebind {
typedef dummy_allocator<U,
typename std::allocator_traits<base_allocator>::template rebind_alloc<U>> other;
};
template <typename T1, typename ...TV>
struct is_dummy : std::is_same<typename std::decay<T1>::type, dummy_allocator<T, base_allocator>>{
};
template<typename... Args>
dummy_allocator(Args &&... args) requires (std::is_constructible_v<base_allocator, Args...>) : alloc(std::forward<Args>(args)...) {}
template <typename U, typename A> friend class dummy_allocator;
// Construct a dummy allocator from another dummy allocator with the same base_allocator but with different type.
template <typename U>
dummy_allocator(
const dummy_allocator<U,
typename std::allocator_traits<base_allocator>::template rebind_alloc<U>>& other) noexcept :
alloc(other.alloc) {}
dummy_allocator() = default;
dummy_allocator(const dummy_allocator& a) : alloc(a.alloc) {}
[[nodiscard]] T *allocate(std::size_t n) {
T *p = alloc.allocate(n);
return p;
}
void deallocate(T *p, std::size_t size) noexcept {
alloc.deallocate(p, size);
}
private:
base_allocator alloc;
};
In particular, these lines here:
template <typename U, typename A> friend class dummy_allocator;
// Construct a dummy allocator from another dummy allocator with the same base_allocator but with different type.
template <typename U>
dummy_allocator(
const dummy_allocator<U,
typename std::allocator_traits<base_allocator>::template rebind_alloc<U>>& other) noexcept :
alloc(other.alloc) {}
Because we need to have constructors between different underlying types for allocators.
I'm trying to use mpl_list in for_each
#include <boost/mpl/list.hpp>
#include <algorithm>
#include <boost/mpl/for_each.hpp>
#include <string>
#include <istream>
#include <ostream>
#include <sstream>
#include <iostream>
#include <boost/mpl/range_c.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/vector_c.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/bind.hpp>
using namespace std;
namespace mpl = boost::mpl;
typedef mpl::range_c<char,1,5> range5;
typedef mpl::list<
mpl::int_<1>
, mpl::int_<5>
, mpl::int_<31>
, mpl::int_<14>
, mpl::int_<51>
> inp_type;
typedef mpl::list<
mpl::int_<1>
, mpl::int_<5>
, mpl::int_<31>
, mpl::int_<14>
, mpl::int_<51>
> out_type;
template <class T> struct id {};
struct do_this_wrapper {
static char stat_c ;
typedef void result_type;
template<typename U> inline void operator()(int i, U )
{
if (i == U::value)
{
do_this_wrapper::stat_c = mpl::at_c<out_type,U::value>::type::value;
}
}
};
char do_this_wrapper::stat_c ;
int main()
{
int x;
std::cin>>x;
boost::mpl::for_each<inp_type>(boost::bind(do_this_wrapper(), x, _1));
return do_this_wrapper::stat_c;
};
I've got these errors
*... /usr/include/boost/mpl/list/aux_/iterator.hpp: In instantiation of ‘struct boost::mpl::deref >’: /usr/include/boost/mpl/aux_/at_impl.hpp:37:45: required from ‘struct boost::mpl::at_impl::apply, mpl_::int_<5>, mpl_::int_<31>, mpl_::int_<14>, mpl_::int_<51> >, mpl_::long_<5l> >’ /usr/include/boost/mpl/at.hpp:42:8: required from ‘struct boost::mpl::at_c, mpl_::int_<5>, mpl_::int_<31>, mpl_::int_<14>, mpl_::int_<51> >, 5l>’ ../src/TestProj3.cpp:2664:41: required from ‘void do_this_wrapper::operator()(int, U) [with U = mpl_::int_<5>]’ /usr/include/boost/bind/bind.hpp:313:34: required from ‘void boost::_bi::list2::operator()(boost::_bi::type, F&, A&, int) [with F = do_this_wrapper; A = boost::_bi::list1&>; A1 = boost::_bi::value; A2 = boost::arg<1>]’ /usr/include/boost/bind/bind_template.hpp:32:59: required from ‘boost::_bi::bind_t::result_type boost::_bi::bind_t::operator()(A1&) [with A1 = mpl_::int_<5>; R = boost::_bi::unspecified; F = do_this_wrapper; L = boost::_bi::list2, boost::arg<1> >; boost::_bi::bind_t::result_type = void]’ /usr/include/boost/mpl/for_each.hpp:75:25: required from ‘static void boost::mpl::aux::for_each_impl::execute(Iterator*, LastIterator*, TransformFunc*, F) [with Iterator = boost::mpl::l_iter, mpl_::int_<31>, mpl_::int_<14>, mpl_::int_<51> > >; LastIterator = boost::mpl::l_iter; TransformFunc = boost::mpl::identity; F = boost::_bi::bind_t, boost::arg<1> > >]’ /usr/include/boost/mpl/for_each.hpp:79:111: required from ‘static void boost::mpl::aux::for_each_impl::execute(Iterator*, LastIterator*, TransformFunc*, F) [with Iterator = boost::mpl::l_iter, mpl_::int_<5>, mpl_::int_<31>, mpl_::int_<14>, mpl_::int_<51> > >; LastIterator = boost::mpl::l_iter; TransformFunc = boost::mpl::identity; F = boost::_bi::bind_t, boost::arg<1> > >]’ /usr/include/boost/mpl/for_each.hpp:101:97: required from ‘void boost::mpl::for_each(F, Sequence*, TransformOp*) [with Sequence = boost::mpl::list, mpl_::int_<5>, mpl_::int_<31>, mpl_::int_<14>, mpl_::int_<51> >; TransformOp = boost::mpl::identity; F = boost::_bi::bind_t, boost::arg<1> > >]’ /usr/include/boost/mpl/for_each.hpp:111:38: required from ‘void boost::mpl::for_each(F, Sequence*) [with Sequence = boost::mpl::list, mpl_::int_<5>, mpl_::int_<31>, mpl_::int_<14>, mpl_::int_<51> >; F = boost::_bi::bind_t, boost::arg<1> > >]’ ../src/TestProj3.cpp:2678:77: required from here /usr/include/boost/mpl/list/aux_/iterator.hpp:39:33: error: no type named ‘item’ in ‘struct boost::mpl::l_end’ typedef typename Node::item type; ^ /usr/include/boost/mpl/list/aux_/iterator.hpp: In instantiation of ‘struct boost::mpl::next >’: /usr/include/boost/mpl/aux_/preprocessed/gcc/advance_forward.hpp:68:44: required from ‘struct boost::mpl::aux::advance_forward<4l>::apply
/usr/include/boost/mpl/aux_/preprocessed/gcc/apply_wrap.hpp:36:8:
required from ‘struct boost::mpl::apply_wrap1, boost::mpl::l_iter > > >’ /usr/include/boost/mpl/aux_/preprocessed/gcc/advance_forward.hpp:83:21: required from ‘struct boost::mpl::aux::advance_forward<27l>::apply ’ /usr/include/boost/mpl/aux_/preprocessed/gcc/apply_wrap.hpp:36:8:
required from ‘struct boost::mpl::apply_wrap1, boost::mpl::l_iter > > >’ /usr/include/boost/mpl/aux_/preprocessed/gcc/advance_forward.hpp:92:21: required from ‘struct boost::mpl::aux::advance_forward<31l>::apply, mpl_::int_<5>, mpl_::int_<31>, mpl_::int_<14>, mpl_::int_<51> > > >’ /usr/include/boost/mpl/aux_/preprocessed/gcc/apply_wrap.hpp:36:8: [ skipping 7 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ] /usr/include/boost/bind/bind_template.hpp:32:59: required from ‘boost::_bi::bind_t::result_type boost::_bi::bind_t::operator()(A1&) [with A1 = mpl_::int_<31>; R = boost::_bi::unspecified; F = do_this_wrapper; L = boost::_bi::list2, boost::arg<1> >; boost::_bi::bind_t::result_type = void]’ /usr/include/boost/mpl/for_each.hpp:79:111: recursively required from ‘static void boost::mpl::aux::for_each_impl::execute(Iterator*, LastIterator*, TransformFunc*, F) [with Iterator = boost::mpl::l_iter, mpl_::int_<31>, mpl_::int_<14>, mpl_::int_<51> > >; LastIterator = boost::mpl::l_iter; TransformFunc = boost::mpl::identity; F = boost::_bi::bind_t, boost::arg<1> > >]’ /usr/include/boost/mpl/for_each.hpp:79:111: required from ‘static void boost::mpl::aux::for_each_impl::execute(Iterator*, LastIterator*, TransformFunc*, F) [with Iterator = boost::mpl::l_iter, mpl_::int_<5>, mpl_::int_<31>, mpl_::int_<14>, mpl_::int_<51> > >; LastIterator = boost::mpl::l_iter; TransformFunc = boost::mpl::identity; F = boost::_bi::bind_t, boost::arg<1> > >]’ /usr/include/boost/mpl/for_each.hpp:101:97: required from ‘void boost::mpl::for_each(F, Sequence*, TransformOp*) [with Sequence = boost::mpl::list, mpl_::int_<5>, mpl_::int_<31>, mpl_::int_<14>, mpl_::int_<51> >; TransformOp = boost::mpl::identity; F = boost::_bi::bind_t, boost::arg<1> > >]’ /usr/include/boost/mpl/for_each.hpp:111:38: required from ‘void boost::mpl::for_each(F, Sequence*) [with Sequence = boost::mpl::list, mpl_::int_<5>, mpl_::int_<31>, mpl_::int_<14>, mpl_::int_<51> >; F = boost::_bi::bind_t, boost::arg<1> > >]’ ../src/TestProj3.cpp:2678:77: required from here /usr/include/boost/mpl/list/aux_/iterator.hpp:45:43: error: no type named ‘next’ in ‘struct boost::mpl::l_end*’
Would be thankfull for detailed descriprtion or sth to read. Thanks in advance
The issue is
do_this_wrapper::stat_c = mpl::at_c<out_type,U::value>::type::value;
This is asking MPL to find the element at the position in out_type that corresponds to the value of U. Since the inp_type has a value of 51 (among other things), it's failing be able to return the element in position 51 in out_type, since it only contains 5 elements.
Personally, I would attempt to join the two sequences into an associative sequence (mpl::map), and then use for_each over that.