I am writing a library that has compile time polymorphic objects templated on unsigned integers,
template<unsigned level> class Foo { ... };
, i.e. Foo<0>, Foo<1>, Foo<2> etc up to a certain number determined by each application . Let's say our application specifies the need for numbers 0, 1,..., n.
I need to be able to construct a tuple of vectors of pointers to these objects: std::tuple<std::vector<Foo<n> *>, std::vector<Foo<n-1> *>, ..., vector<Foo<0> *>> by invoking a variadic template function, schematically:
template <unsigned n>
std::tuple<unsigned n, unsigned... Args> create_vector_tuple<n>()
or something like it.
My intuition tells me that this should be possible to accomplish though I am way out of my depth here.
I would be very grateful if anyone could point me to a way to implement this!
Something like the following:
#include <cstdio>
#include <tuple>
#include <vector>
#include <utility>
template<unsigned level> struct Foo {};
template<unsigned N, unsigned... Ns>
std::tuple<std::vector<Foo<N - Ns>*>...> create_vector_tuple_imp(std::integer_sequence<unsigned, Ns...>) {
return {};
}
template <unsigned n>
auto create_vector_tuple() {
return create_vector_tuple_imp<n>(std::make_integer_sequence<unsigned, n>{});
}
template<class T>
void print() {
std::printf("%s\n", __PRETTY_FUNCTION__);
}
int main() {
auto t = create_vector_tuple<10>();
print<decltype(t)>();
}
Outputs:
void print() [with T = std::tuple<std::vector<Foo<10u>*, std::allocator<Foo<10u>*> >, std::vector<Foo<9u>*, std::allocator<Foo<9u>*> >, std::vector<Foo<8u>*, std::allocator<Foo<8u>*> >, std::vector<Foo<7u>*, std::allocator<Foo<7u>*> >, std::vector<Foo<6u>*, std::allocator<Foo<6u>*> >, std::vector<Foo<5u>*, std::allocator<Foo<5u>*> >, std::vector<Foo<4u>*, std::allocator<Foo<4u>*> >, std::vector<Foo<3u>*, std::allocator<Foo<3u>*> >, std::vector<Foo<2u>*, std::allocator<Foo<2u>*> >, std::vector<Foo<1u>*, std::allocator<Foo<1u>*> > >]
Related
Aim: To do compile-time recursion over a given set of template template classes to compute a given function.
Problem: I could not devise how the template arguments should look like for run_tests function.
MWE: Please consider the following code example working for a given set of built-in types (This example is based on an answer in stackoverflow, which I couldn't find it back so far - I will add the link when I find it):
#include <tuple>
#include <type_traits>
#include <iostream>
#include <vector>
template<class Type>
void test(Type)
{
// Do nothing
std::cout<< "I survived 2020" << std::endl;
}
// Do compile-time recursion over the given types
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
run_tests
(
const std::tuple<Tp...>& types
)
{}
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
run_tests
(
const std::tuple<Tp...>& types
)
{
test(std::get<I>(types));
run_tests<I + 1, Tp...>(types);
}
int main(int argc, char *argv[])
{
const std::tuple<int, double> types // working
// const std::tuple // not working
// <
// std::vector<std::vector<int>>,
// std::vector<std::vector<double>>
// > types
(
std::make_tuple(nullptr, nullptr)
);
run_tests(types);
return 0;
}
Attempts: I have commented out the commented piece of code above, i.e.:
// const std::tuple<int, double> types // working
const std::tuple // not working
<
std::vector<std::vector<int>>,
std::vector<std::vector<double>>
> types
The above threw the following as the first compilation error:
error: no matching function for call to ‘std::tuple<std::vector<std::vector<int,
std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > >,
std::vector<std::vector<double, std::allocator<double> >,
std::allocator<std::vector<double, std::allocator<double> > > >
>::tuple(std::tuple<std::nullptr_t, std::nullptr_t>)’
);
Your issue is with initialization of the tuple, nullptr are not valid for std::vector.
You might use default initialization:
const std::tuple<int, double> type1{};
const std::tuple<std::vector<std::vector<int>>,
std::vector<std::vector<double>>> type2{};
Demo
I am trying to write a wrapper class to a Cartesian product iterator provided by Miranda Conrado (source code can be found on GitHub). For convenience I will quote relevant bits of code here, too.
My class can be constructed in two ways - one straightforward, by just forwarding the Containers to the product_iterator constructor, the other is a bit trickier: it takes a number of tuples describing the linspace needed to create the Containers and then constructs the iterator out of them. It is here where I hit a dead-end.
Here is some code.
First, some relevant headers from Conrado's class product_iterator:
// product_iterator.hpp
template <class... Containers>
class product_iterator:
...
public:
product_iterator();
product_iterator(product_iterator const& other);
product_iterator(Containers const&... containers);
~product_iterator();
product_iterator const& operator=(product_iterator const& other);
....
};
template <class... Containers>
product_iterator<Containers...>
make_product_iterator(Containers const&... containers) {
return product_iterator<Containers...>(containers...);
}
And here is my class:
// gridsearch.hpp
typedef std::unordered_map<std::string, Real> result_type;
typedef std::vector<result_type> resultgrid_type;
template <class... Containers>
class GridSearchIterator {
typedef std::array<std::string,
std::tuple_size<std::tuple<Containers...> >::value>
argname_type;
public:
GridSearchIterator() : product_it(product_iterator<Containers...>()),
argnames(argname_type()) {}
GridSearchIterator(const argname_type& names,
const Containers& ...containers);
template <class... TupleTypes>
static GridSearchIterator<Containers...>
initWith(const TupleTypes&& ...tuples);
template<class F, class... Args>
decltype(auto) iterate(F func, Args&&... params);
private:
template <typename TupleType, size_t... Is>
void product_impl(TupleType&& tuples, std::index_sequence<Is...>);
template <typename TupleType>
const auto& unpack_tuple(TupleType& t, size_t index);
product_iterator<Containers...> product_it;
argname_type argnames;
};
// implementation:
template <class... Containers>
GridSearchIterator<Containers...>::GridSearchIterator(
const argname_type& names,
const Containers& ...containers):
product_it(product_iterator<Containers...>(containers...)),
argnames(names) {}
template <class... Containers>
template <typename... TupleTypes>
GridSearchIterator<Containers...> GridSearchIterator<Containers...>::initWith(const TupleTypes&& ...tuples)
{
GridSearchIterator<Containers...> gsi =
GridSearchIterator<Containers...>();
gsi.product_impl(std::tuple<TupleTypes...>(tuples...),
std::index_sequence_for<TupleTypes...>{});
return gsi;
}
template <class... Containers>
template <typename TupleType, size_t... Is>
void GridSearchIterator<Containers...>::product_impl(TupleType&& tuples,
std::index_sequence<Is...>)
{
product_it = product_iterator<Containers...>(
unpack_tuple(std::get<Is>(tuples), Is)...);
// this is where the problem is; Compiler claims No matching constructor for initialization of 'product_iterator...
}
template <class... Containers>
template <typename TupleType>
const auto& GridSearchIterator<Containers...>::unpack_tuple(TupleType &t,
size_t index)
{
std::string argname;
auto left(0), right(0);
Size step;
std::tie(argname, left, right, step) = t;
argnames[index] = argname;
auto vec = linspace(left, right, step);
return static_cast<const decltype(vec) &>(vec);
}
The function linspace above returns a vector of numbers from left to right evenly spaced by number of steps. It is equivalent to Numpy function np.linspace.
I checked and the call to unpack_tuple() does produce the vectors needed to initialise the product_iterator, yet the compiler disagrees. My guess is the types returned by unpack_tuple() are somewhat different from what the product_iterator constructor expects but I can't figure out what is the problem. Or maybe the problem actually lies elsewhere entirely.
For better understanding, here is how I use the class:
{
...
typedef std::tuple<std::string, int, int, size_t> inttuple;
typedef std::tuple<std::string, double, double, size_t> realtuple;
typedef std::vector<int> intvector;
typedef std::vector<Real> realvector;
inttuple sidespan = std::make_tuple("side",1,1,1);
real tuple takeprofit = std::make_tuple("takeprofit",1.,2.,2);
real tuple stoploss = std::make_tuple("stoploss", -1.,-3.,3);
inttuple period = std::make_tuple("horizon", 100, 100, 1);
auto grid_iter = GridSearchIterator<intvector, realvector, realvector, intvector>
::initWith(std::forward<inttuple>(sidespan),
std::forward<realtuple>(takeprofit),
std::forward<realtuple>(stoploss),
std::forward<inttuple>(period));
...
}
I spent hours trying to solve it so any help or pointers will be highly appreciated, including advice on different implementation.
UPDATE
Sorry, I thought I updated my question yesterday but the changes were not saved for some reason.
Anyway, #max66 answered the question even without additional info. Still, for completeness, here is the linspace() definition
template <typename T>
std::vector<T> linspace(T a, T b, size_t N)
and compiler message:
In file included from /.../main.cpp:17:
/.../gridsearch.hpp:98:18: error: no matching constructor for initialization of 'product_iterator<std::__1::vector<int, std::__1::allocator<int> >, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<int, std::__1::allocator<int> > >'
product_it = product_iterator<Containers...>(unpack_tuple(std::get<Is>(tuples), Is)...);
/.../gridsearch.hpp:91:9: note: in instantiation of function template specialization 'GridSearchIterator<std::__1::vector<int, std::__1::allocator<int> >, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<int, std::__1::allocator<int> > >::product_impl<std::__1::tuple<std::__1::tuple<std::__1::basic_string<char>, int, int, unsigned long>, std::__1::tuple<std::__1::basic_string<char>, double, double, unsigned long>, std::__1::tuple<std::__1::basic_string<char>, double, double, unsigned long>, std::__1::tuple<std::__1::basic_string<char>, int, int, unsigned long> >, 0, 1, 2, 3>' requested here gsi.product_impl(std::tuple<TupleTypes...>(tuples...), std::index_sequence_for<TupleTypes...>{});
/.../main.cpp:90:88: note: in instantiation of function template specialization 'GridSearchIterator<std::__1::vector<int, std::__1::allocator<int> >, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<int, std::__1::allocator<int> > >::initWith<std::__1::tuple<std::__1::basic_string<char>, int, int, unsigned long>, std::__1::tuple<std::__1::basic_string<char>, double, double, unsigned long>, std::__1::tuple<std::__1::basic_string<char>, double, double, unsigned long>, std::__1::tuple<std::__1::basic_string<char>, int, int, unsigned long> >' requested here auto grid_iter = GridSearchIterator<intvector, realvector, realvector, intvector>::initWith(std::forward<inttuple>(sidespan),
In file included from /.../main.cpp:17:
In file included from /.../gridsearch.hpp:22:
/.../product_iterator.hpp:73:7: note: candidate constructor not viable: no known conversion from 'vector<int, allocator<int>>' to 'const vector<double, allocator<double>>' for 2nd argument product_iterator(Containers const&... containers);
It's difficult to check/verify/propose a correct code if you don't propose a complete example.
Anyway you have the error ("no matching constructor") in this line
product_it = product_iterator<Containers...>(
unpack_tuple(std::get<Is>(tuples), Is)...);
where, if I understand correctly, Containers... is intvector, realvector, realvector, intvector a.k.a. std::vector<int>, std::vector<Real>, std::vector<Real>, std::vector<int> (where I suppose Real is an alias for double).
The only variadic constructor for product_iterator is the one receiving Containers const&... containers so I suppose is the one you want to match.
It seems to me that the problem is that unpack_tuple()
template <class... Containers>
template <typename TupleType>
const auto& GridSearchIterator<Containers...>::unpack_tuple(TupleType &t,
size_t index)
{
std::string argname;
auto left(0), right(0);
Size step;
std::tie(argname, left, right, step) = t;
argnames[index] = argname;
auto vec = linspace(left, right, step);
return static_cast<const decltype(vec) &>(vec);
}
return ever a intVector const & (std::vector<int> const &). Also when called with a realVector (std::vector<double>, I suppose).
This (if I'm not wrong) it's because you define left and right as auto and initializing they whit an int
auto left(0), right(0);
you get a couple of int also when TupleType contains Real elements in second and third position.
So when you obtain vec
auto vec = linspace(left, right, step);
you obtain (I suppose) a std::vector<int>; ever; also when you should obtain a std::vector<Real>.
Suggestion: define left and right with the correct type that depends from TupleType.
By example (caution: code not tested)
using lr_type = typename std::tuple_element<1u, TupleType>::type;
lr_type left, right;
Starting from C++14 you can use std::tuple_element_t and the `using can be simplified as follows
using lr_type = std::tuple_element_t<1u, TupleType>;
If you can use C++17, you can use structured binding and all become a lot simpler
template <typename TupleType>
const auto& GridSearchIterator<Containers...>::unpack_tuple(TupleType &t,
size_t index)
{
auto [argname, left, right, step] = t;
argnames[index] = argname;
auto vec = linspace(left, right, step);
return static_cast<const decltype(vec) &>(vec);
}
Off Topic: are you sure that is a good idea unpack_tuple() returning a const reference to a value that is destroyed at the end of execution of the method ?
I have the following class with an unordered_map member, and a hash function defined for pair<int,int>
class abc
{public :
unordered_map < pair<int,int> , int > rules ;
unsigned nodes;
unsigned packet ;
};
namespace std {
template <>
class hash < std::pair< int,int> >{
public :
size_t operator()(const pair< int, int> &x ) const
{
size_t h = std::hash<int>()(x.first) ^ std::hash<int>()(x.second);
return h ;
}
};
}
But I am getting the following errors :
error: invalid use of incomplete type ‘struct std::hash<std::pair<int, int> >
error: declaration of ‘struct std::hash<std::pair<int, int> >
error: type ‘std::__detail::_Hashtable_ebo_helper<1, std::hash<std::pair<int, int> >, true>’ is not a direct base of ‘std::__detail::_Hash_code_base<std::pair<int, int>, std::pair<const std::pair<int, int>, int>, std::__detail::_Select1st, std::hash<std::pair<int, int> >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, true>’
Unfortunately, this program has undefined behavior. C++11 §17.6.4.2.1:
A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.
hash<pair<int,int>> depends on primitive and standard library types only. This is easily worked around by defining your hash class outside of namespace std, and using that hash explicitly in your map declaration:
struct pairhash {
public:
template <typename T, typename U>
std::size_t operator()(const std::pair<T, U> &x) const
{
return std::hash<T>()(x.first) ^ std::hash<U>()(x.second);
}
};
class abc {
std::unordered_map<std::pair<int,int>, int, pairhash> rules;
};
EDIT: I've used xor to combine the hashes of the pair members here because I'm lazy, but for serious use xor is a fairly crappy hash combining function.
I prefer to rely on the standard implementation of std::hash<uintmax_t> to mix hashes of components of an std::pair:
#include <functional>
#include <utility>
struct hash_pair final {
template<class TFirst, class TSecond>
size_t operator()(const std::pair<TFirst, TSecond>& p) const noexcept {
uintmax_t hash = std::hash<TFirst>{}(p.first);
hash <<= sizeof(uintmax_t) * 4;
hash ^= std::hash<TSecond>{}(p.second);
return std::hash<uintmax_t>{}(hash);
}
};
I'm trying to do the following (only relevant parts of code below):
template<typename ContainerType>
struct IsContainerCheck : is_container<ContainerType>
{
static constexpr char* err_value = "Type is not a container model";
};
namespace _check_concept {
template<typename ResultType>
struct run {
constexpr static int apply() {
static_assert(false, IsContainerCheck<ResultType>::err_value)
return 0;
}
};
template<>
struct run<true_t> {
constexpr static int apply() {
return 0;
}
};
}
This fails because the static_assert allows only literals to be printed. The same is with BOOST_STATIC_ASSERT_MSG macro.
So my question is - is there any way to output a constexpr string during compilation?
If there is a gcc extension providing this functionality that would also be great.
Used compiler gcc 4.8.1
GCC does not provide such a mechanism as you want. However you will not need
it if you are able to refactor your code somewhat as illustrated in the
following program. (I have filled in a few gaps so as to give us a
compilable example):
#include <type_traits>
#include <vector>
template<typename ContainerType>
struct is_container
{
static bool const value = false;
};
template<>
struct is_container<std::vector<int>>
{
static bool const value = true;
};
template<typename ContainerType>
struct IsContainerCheck // : is_container<ContainerType> <- Uneccessary
{
static_assert(is_container<ContainerType>::value,
"Type is not a container model");
};
namespace _check_concept {
template<typename ResultType>
struct run {
constexpr static int apply() {
return (IsContainerCheck<ResultType>(),0);
}
};
// No such specialization is necessary. Delete it.
// template<>
// struct run<true_t> {
// constexpr static int apply() {
// return 0;
// }
//};
}
using namespace _check_concept;
int main(int argc, char **argv)
{
auto verdict0 = run<std::vector<int>>::apply();
(void)verdict0;
// The following line will static_assert: "Type is not a container model"
auto verdict1 = run<float>::apply();
(void)verdict1;
return 0;
}
In your specialization _check_concept::struct run<true_t> I presume that
true_t is not an alias or equivalent of std::true_type, but rather
just a place-holder for some ResultType that is a container type. As
the test program shows, no such specialization is now necessary, because
IsContainerCheck<ResultType>() will static_assert, or not, depending
on ResultType, in the unspecialized run<ResultType>::apply().
I had some time (and a good liqueur to come along with it) to think more about the problem. This is what I came up with:
namespace _details {
struct PassedCheck {
constexpr static int printError () {
return 0; //no error concept check passed
}
};
template<template<typename> class ConceptCheck, typename ...ModelTypes>
struct check_concept_impl;
template<template<typename> class ConceptCheck, typename FirstType, typename ...ModelTypes>
struct check_concept_impl<ConceptCheck, FirstType, ModelTypes...> : mpl::eval_if< typename ConceptCheck<FirstType>::type,
check_concept_impl<ConceptCheck, ModelTypes...>,
mpl::identity<ConceptCheck<FirstType>>>
{ };
template<template<typename> class ConceptCheck, typename LastType>
struct check_concept_impl<ConceptCheck, LastType> : mpl::eval_if<typename ConceptCheck<LastType>::type,
mpl::identity<PassedCheck>,
mpl::identity<ConceptCheck<LastType>>>
{ };
}
template<template<typename> class ConceptCheck, typename ...ModelTypes>
struct check_concept {
private:
typedef typename _details::check_concept_impl<ConceptCheck, ModelTypes...>::type result_type;
public:
// the constexpr method assert produces shorter, fixed depth (2) error messages than a nesting assert in the trait solution
// the error message is not trahsed with the stack of variadic template recursion
constexpr static int apply() {
return result_type::printError();
}
};
template<typename ContainerType>
struct IsContainerCheck : is_container<ContainerType>
{
template<typename BoolType = false_t>
constexpr static int printError () {
static_assert(BoolType::value, "Type is not a container model");
return 0;
}
};
and the usage:
check_concept<IsContainerCheck, std::vector<int>, std::vector<int>, float, int>::apply();
The solution is probably not the most elegant one but I it keeps the assert message short:
In file included from ../main.cpp:4:0:
../constraint.check.hpp: In instantiation of ‘static constexpr int IsContainerCheck::printError() [with BoolType = std::integral_constant; ContainerType = float]’:
../constraint.check.hpp:61:34: required from ‘static constexpr int check_concept::apply() [with ConceptCheck = IsContainerCheck; ModelTypes = {std::vector >, std::vector >, float, int}]’
../main.cpp:25:83: required from here
../constraint.check.hpp:74:3: error: static assertion failed: Type is not a container model
static_assert(BoolType::value, "Type is not a container model");
The assert is issued in a constexpr method after the check_concept template specialization has been done. Embedding the static assert directly into the template class definition would drag the whole check_concept_impl recursion stack into the error message.
So changing the IsContainerCheck trait to something like (rest of the changes omitted for readibility):
template<typename ContainerType>
struct IsContainerCheck
{
static_assert(is_container<ContainerType>::type::value, "Type is not a container model");
};
would yield an error
../constraint.check.hpp: In instantiation of ‘struct IsContainerCheck’:
../constraint.check.hpp:36:9: required from ‘struct _details::check_concept_impl’
/usr/include/boost/mpl/eval_if.hpp:38:31: required from ‘struct boost::mpl::eval_if, _details::check_concept_impl, boost::mpl::identity > > >’
../constraint.check.hpp:36:9: required from ‘struct _details::check_concept_impl >, float, int>’
/usr/include/boost/mpl/eval_if.hpp:38:31: required from ‘struct boost::mpl::eval_if, _details::check_concept_impl >, float, int>, boost::mpl::identity > > >’
../constraint.check.hpp:36:9: required from ‘struct _details::check_concept_impl >, std::vector >, float, int>’
../constraint.check.hpp:53:84: required from ‘struct check_concept >, std::vector >, float, int>’
../main.cpp:25:81: required from here
../constraint.check.hpp:72:2: error: static assertion failed: Type is not a container model
static_assert(is_container::type::value, "Type is not a container model");
As you can see each recursive eval_if call is emended in the error description which is bad because it makes the error message dependent from the amount and type of template parameters.
I am having a problem in some code using type_traits from boost.
It is quite a complex part of the code, but I could isolate the part that gives the compilation error:
template<const size_t maxLen>
class MyString {
public:
typedef boost::conditional<(maxLen > 0), char[maxLen+1], std::string> ObjExternal;
};
template <class T>
class APIBase {
public:
typedef T obj_type;
typedef typename T::ObjExternal return_type;
};
template <class T>
int edit(const T& field, const typename T::return_type& value)
{
return 0;
}
int myFunction()
{
APIBase<MyString<10> > b;
char c[11];
return edit(b, c);
}
This gives the following error:
test.cpp: In function ‘int myFunction()’:
tes.cpp:109: error: no matching function for call to ‘edit(APIBase >&, char [11])’
tes.cpp:100: note: candidates are: int edit(const T&, const typename T::return_type&) [with T = APIBase >]
However, if I change the line with the code
char c[11];
by
MyString<10>::ObjExternal c;
it works. Similarly, if instead I change the line
typedef boost::conditional<(maxLen > 0), char[maxLen+1], std::string> ObjExternal;
by
typedef char ObjExternal[maxLen+1];
it also works. I am thinking that it is a problem with boost::conditional, as it seems it is not being evaluated right. Is there a problem in my code, or there is an alternative that can be used instead of boost::conditional to have this functionality?
I am thinking about using the 2nd option, but then I could not use maxLen as 0.
You need to use the member typedef type provided by conditional and not the conditional type itself.
Change:
typedef boost::conditional<(maxLen > 0),
char[maxLen+1],
std::string> ObjExternal;
to:
typedef typename boost::conditional<(maxLen > 0),
char[maxLen+1],
std::string>::type ObjExternal;