Recursive aggregate type configured using many template parameters using std::index_sequence - c++17

There is a class template:
template<std::size_t ID, std::size_t T1, std::size_t T2, std::size_t T3>
class Feature { /* Implementation goes here */ };
All the instantiations of Feature<...> are 'collected' here:
template<typename FEATURE, typename... OTHERS>
class Features<FEATURE, OTHERS...> : public Features<OTHERS...> {
public:
/* Operations defined here */
private:
FEATURE m_feature;
};
All the features are created as follows:
using FeatureConfig = Features<Feature<0, 1, 2, 3>, Feature<1, 4, 5, 6>>;
FeatureConfig m_features;
So far so good. My task is to get rid of those hard coded values in there 1..3, 4..6 etc. The way to do so is to have generated header file which contains the configuration for all the features. Something like:
template<std::size_t> struct Config;
template<>
struct Config<0> {
static constexpr std::size_t t1 { 1 };
static constexpr std::size_t t2 { 2 };
static constexpr std::size_t t3 { 3 };
};
template<>
struct Config<1> {
static constexpr std::size_t t1 { 4 };
static constexpr std::size_t t2 { 5 };
static constexpr std::size_t t3 { 6 };
};
Then I need to change type definition of FeatureConfig somehow to use the specializations of FeatureConfig based on an index (0, 1, ...). My unsuccessfull try is:
template<std::size_t... INDEX_SEQUENCE>
using FeatureConfig = Features<Feature<INDEX_SEQUENCE, Config<INDEX_SEQUENCE>::t1, Config<INDEX_SEQUENCE>::t2, Config<INDEX_SEQUENCE>::t3>...>;
FeatureConfig<std::make_index_sequence<2>> m_features;
It seems I am somehow mixing type and value...
Many thanks in advance to anyone willing to help me fix the incorrect code in my last listing up there.
Cheers Martin

If I understand correctly what do you want...
I propose the declaration (no definition required because is used only inside a decltype()) of the following function
template <std::size_t ... Is>
auto getFeaturesType (std::index_sequence<Is...>)
-> Features<Feature<Is, Config<Is>::t1, Config<Is>::t2, Config<Is>::t3>...>;
Now you can define FeatureConfig simply as follows
template <std::size_t N>
using FeatureConfig
= decltype(getFeaturesType(std::make_index_sequence<N>{}));
The following is a full compiling (simplified) example
#include <type_traits>
#include <utility>
template <std::size_t, std::size_t, std::size_t, std::size_t>
struct Feature { };
template <typename...>
struct Features
{ };
template <typename F, typename... Os>
struct Features<F, Os...> : public Features<Os...>
{ F m_feature; };
template <std::size_t N>
struct Config
{
static constexpr std::size_t t1 { N*3u };
static constexpr std::size_t t2 { 1u + N*3u };
static constexpr std::size_t t3 { 2u + N*3u };
};
template <std::size_t ... Is>
auto getFeaturesType (std::index_sequence<Is...>)
-> Features<Feature<Is, Config<Is>::t1, Config<Is>::t2, Config<Is>::t3>...>;
template <std::size_t N>
using FeatureConfig
= decltype(getFeaturesType(std::make_index_sequence<N>{}));
int main ()
{
using T1 = FeatureConfig<2u>;
using T2 = Features<Feature<0u, 0u, 1u, 2u>, Feature<1u, 3u, 4u, 5u>>;
static_assert( std::is_same<T1, T2>::value, "!" );
}
If I understand correctly how do you use Config (if t1 is ever N*3u, if if t2 is ever 1u+N*3u and if t3 is ever 2u+N*3u), you can avoid Config at all and write getFeaturesType as follows
template <std::size_t ... Is>
auto getFeaturesType (std::index_sequence<Is...>)
-> Features<Feature<Is, Is*3u, Is*3u+1u, Is*3u+2u>...>;

Related

How to defer expanding a parameter pack?

I was toying around with tuples. I wanted to zip an arbitrary number of tuples. There is probably a better way to do that than what I came up with, but my solution led me to a problem that is interesting in itself.
Sometimes, you want to expand one parameter pack at a time, and those parameter packs can be template arguments and function arguments in the same call. I could not find an obvious way to expand Is without expanding ts, besides stuffing ts into a std::tuple and unpacking it again using std::get<I>.
Obviously, it's preferable not to split one function into five functions. I know I could use lambdas, but I'm not sure that would be any cleaner.
Is there a nice way to defer expanding ts?
https://godbolt.org/z/sY5xMTa7P
#include <iostream>
#include <tuple>
#include <string_view>
template <typename T, typename... Ts>
auto get_first(T t, Ts...) {
return t;
}
template <size_t I, typename... Ts>
auto zip2_impl4(Ts... ts) {
return std::make_tuple(std::get<I>(ts)...);
}
template <size_t I, typename T, size_t... Is>
auto zip2_impl3(T t, std::index_sequence<Is...>) {
return zip2_impl4<I>(std::get<Is>(t)...);
}
template <size_t I, typename T>
auto zip2_impl2(T t) {
using size = std::tuple_size<T>;
using seq = std::make_index_sequence<size::value>;
return zip2_impl3<I>(t, seq{});
}
template <size_t... Is, typename... Ts>
auto zip2_impl(std::index_sequence<Is...> seq, Ts... ts) {
// need to defer expanding the pack ts,
// because the packs Is and ts need to expand separately
auto t = std::make_tuple(ts...);
return std::make_tuple(zip2_impl2<Is>(t)...);
}
template <typename... Ts>
auto zip2(Ts... ts) {
using size = std::tuple_size<decltype(get_first(ts...))>;
using seq = std::make_index_sequence<size::value>;
return zip2_impl(seq{}, ts...);
}
int main() {
using namespace std::literals;
auto ints = std::make_tuple(1,2,3);
auto svs1 = std::make_tuple("a"sv, "b"sv, "c"sv);
auto svs2 = std::make_tuple("d"sv, "e"sv, "f"sv);
auto zipped = zip2(ints, svs1, svs2);
std::apply([](auto... args) {
(std::apply([](auto... args) {
((std::cout << args), ...);
}, args), ...);
}, zipped);
return 0;
}
output: 1ad2be3cf
If I understood you correctly:
https://godbolt.org/z/e4fvr45rz
#include <type_traits>
#include <tuple>
#include <cstddef>
template <size_t Indx,typename ... Tuples>
constexpr auto zip(Tuples ... tuples)
{
return std::make_tuple(std::get<Indx>(tuples)...);
}
template <typename ... Tuples, size_t ... Indx>
constexpr auto zip(std::index_sequence<Indx...>, Tuples... tuples)
{
// will expand simultaneously, not what you want...
// return std::make_tuple(std::make_tuple(std::get<Indx>(tuples)...));
return std::make_tuple(zip<Indx>(tuples...)...);
}
template <typename ... Tuples>
constexpr auto zip(Tuples ... tuples)
{
return zip(std::make_index_sequence<sizeof...(Tuples)>(), tuples...);
}
#include <iostream>
#include <string_view>
int main()
{
using namespace std::literals;
auto ints = std::make_tuple(1,2,3);
auto svs1 = std::make_tuple("a"sv, "b"sv, "c"sv);
auto svs2 = std::make_tuple("d"sv, "e"sv, "f"sv);
auto zipped = zip(ints, svs1, svs2);
std::apply([](auto... args) {
(std::apply([](auto... args) {
((std::cout << args), ...);
std::cout << std::endl;
}, args), ...);
}, zipped);
return 0;
}
Output:
Program returned: 0
1ad
2be
3cf
So there is a way, I guess. You have to have your parameter packs in different contexts: one in the function arguments, another in the template argumetns. If you try to expand both packs in one "context" you will get a compile-time error:
// this will not compile
// return std::make_tuple(std::make_tuple(std::get<Indx>(tuples)...)...);
// and this will expand them simultaneously, giving you "1bf" as a result
return std::make_tuple(std::make_tuple(std::get<Indx>(tuples)...));
UPDATE:
https://godbolt.org/z/9E1zj5q4G - more generic solution:
template <typename FirstTuple, typename ... Tuples>
constexpr auto zip(FirstTuple firstTuple, Tuples ... tuples)
{
return zip(std::make_index_sequence<std::tuple_size_v<FirstTuple>>(), firstTuple, tuples...);
}
int main()
{
using namespace std::literals;
auto ints = std::make_tuple(1,2,3);
auto svs1 = std::make_tuple("a"sv, "b"sv, "c"sv);
auto svs2 = std::make_tuple("d"sv, "e"sv, "f"sv);
auto svs3 = std::make_tuple("g"sv, "h"sv, "i"sv);
auto zipped = zip(ints, svs1, svs2, svs3);
std::apply([](auto... args) {
(std::apply([](auto... args) {
((std::cout << args), ...);
std::cout << std::endl;
}, args), ...);
}, zipped);
return 0;
}
Output:
1adg
2beh
3cfi
Instead of number of tuples you pass a number of elements. You have to be sure that each tuple has at least as many elements as the first one. Extra elements will be truncated. If there are too few elements, there will be a compilation error from std::get<>.

C++17 alternative to C++20 "requires" keyword

C++20 introduced many improvements like requires, concepts, constraints, modules and much more - functionality you really miss in C++17.
How can a scenario having conditional constructors be implemented in C++17, that could look like the following C++20 example (using requires)?
template <typename T> concept has_type = requires { typename T::type; };
template <typename T>
class someClass {
public:
using data_t = typename std::conditional_t<has_type<T>, T, std::type_identity<T> >::type;
constexpr someClass(T const& _a, T const& _b) requires std::is_arithmetic_v<T> : a{_a}, b{_b} {}
constexpr someClass(data_t const& _a, data_t const& _b,) requires has_type<T> : a{_a}, b{_b} {}
private:
const data_t a, b;
};
One constructor has to be used in case of T is an arithmetic type (int, float, double, ...).
Another constructor needs to catch the case of T being a class/struct having a nested type alias (e.g. struct x { using type=float; };).
Using SFINAE
template <typename, typename = std::void_t<>>
struct HasTypeT : std::false_type {};
template <typename T>
struct HasTypeT<T, std::void_t<typename T::type>> : std::true_type {};
template <typename T>
struct type_identity {
using type = T;
};
template <typename T>
class someClass {
public:
using data_t = typename std::conditional_t<HasTypeT<T>::value, T, type_identity<T> >::type;
template <typename U = T, typename = std::enable_if_t<std::is_arithmetic_v<U>>>
constexpr someClass(T const& _a, T const& _b) : a{_a}, b{_b} {}
template <typename U = T, typename = std::enable_if_t<HasTypeT<U>::value>>
constexpr someClass(typename U::type const& _a, typename U::type const& _b) : a{_a}, b{_b} {}
private:
const data_t a, b;
};
Demo

Could you demo a very simple C++ example about Symbolic variable and Jacobian by converting Pydrake code to C++ version?

I am trying to convert the following pydrake code to C++ version. Unfortunately,I get lost in the very rigorous C++ API documentation. Could you help to convert the following code into C++ version for a tutorial? Thank you so much!
import pydrake.math as drake_math
import pydrake.symbolic as sym
def cost_stage(x):
m = sym if x.dtype == object else np # Check type for autodiff
cost = m.sqrt(x[0]**2 + x[1]**2 )
return cost
x_sym = np.array([sym.Variable("x_{}".format(i)) for i in range(n_x)])
x = x_sym
l = cost_stage(x)
self.l_x = sym.Jacobian([l], x).ravel()
Since you used the name "cost", I suppose you want to use this as a cost in drake's MathematicalProgram, so I created MyCost class which can be used in Drake's MathematicalProgram. If you don't want to use MathematicalProgram later, you could just use the templated function DoEvalGeneric only without the class MyCost.
Here is the C++ pseudo-code (I didn't compile or run the code, so it is highly likely there are bugs in the code, but you get the idea)
#include "drake/solvers/cost.h"
#include "drake/common/symbolic.h"
class MyCost : public drake::solvers::Cost {
public:
MyCost() {}
protected:
void DoEval(const Eigen::Ref<const Eigen::VectorXd>& x, Eigen::VectorXd* y) const override {
DoEvalGeneric<double>(x, y);
}
void DoEval(const Eigen::Ref<const drake::AutoDiffVecXd>& x, drake::AutoDiffVecXd* y) const override {
DoEvalGeneric<drake::AutoDiffXd>(x, y)
}
void DoEval(const Eigen::Ref<const drake::VectorX<drake::symbolic::Variable>>& x, drake::VectorX<drake::symbolic::Expression>* y) const override {
DoEvalGeneric<drake::symbolic::Expression>(x.cast<drake::symbolic::Expression>(), y);
}
private:
template <typename T>
void DoEvalGeneric(const Eigen::Ref<const drake::VectorX<T>>& x, drake::VectorX<T>* y) const {
y->resize(1);
using std::sqrt
(*y)(0) = sqrt(x[0] * x[0] + x[1] * x[1]);
}
};
void main() {
const drake::symbolic::Variable x0{"x0"};
const drake::symbolic::Variable x1{"x1"};
drake::Vector2<drake::symbolic::Variable> x(x0, x1);
MyCost cost{};
drake::VectorX<drake::symbolic::Expression> y;
cost.Eval(x, &y);
std::cout << y(0).Jacobian(x) << "\n";
}
Here I created a templated function DoEvalGeneirc to handle the three different scalar types double, AutoDiffXd and symbolic expression. You could see similar patterns in the drake codebase https://github.com/RobotLocomotion/drake/blob/6ee5e9325821277a62bd5cd5456ccf02ca25dab7/solvers/cost.cc#L14-L33
If you don't need to use cost in drake MathematicalProgram, then you can create your cost function in C++ as
#include "drake/common/symbolic.h"
#include <Eigen/Core>
template <typename Derived>
typename Derived::Scalar cost_stage(const Derived& x) {
using std::sqrt;
return sqrt(x[0] * x[0] + x[1] * x[1]);
}
int main() {
const drake::symbolic::Variable x0{"x0"};
const drake::symbolic::Variable x1{"x1"};
drake::Vector2<drake::symbolic::Variable> x(x0, x1);
const drake::symbolic::Expression l = cost_stage(x.cast<drake::symbolic::Expression>());
std::cout << l.Jacobian(x) << "\n";
return 0;
}
Then you can call Jacobian on the return argument of cost_stage.

array<double*> to vector<double> range v3 style on VS2019

I want to convert an array<double*> to a vector<double> so that I can do a ranges::views::concat on two vectors of the same type but I'm having difficulty doing this.
I have the following code:
#include <range/v3/all.hpp>
#include <array>
static constexpr auto max_elements = 2000;
struct PriceInfo
{
std::array<double*, max_elements> prices;
};
auto main() -> int
{
const PriceInfo* const buf_prices = nullptr;
const auto vec = buf_prices->prices
| ranges::views::indirect
| ranges::to_vector;
}
When I compile I'm getting the following errors and I don't understand why as I believe my code is correct, at least it compiles errorfree under gcc.
Error (active) E0349 no operator "|" matches these operands
How do I fix this error please?
FIrst of, your code contains UB, because you had never created PriceInfo.
Second, the error may mean incorrect project configuration, is standard set? is compiler fully compatible with requirements of library? Is it proper fork of library, if that's the case (e.g. there were separate forks for MSVC compilers).
Third, assuming those problems will be solved, this code would segfault unless ALL elements of prices are non-nullptr.
This way it works:
#include <range/v3/all.hpp>
#include <iostream>
#include <array>
static constexpr auto max_elements = 3; // will segfault if there are null pointers
struct PriceInfo
{
std::array<double*, max_elements> prices;
};
auto main() -> int
{
auto a = std::array<double,3>{1.0, 2.0, 3.0};
const PriceInfo* const buf = new PriceInfo{&a[0], &a[1], &a[2]};
const auto vec = buf->prices
| ranges::views::indirect
| ranges::to_vector;
for( auto a : vec)
std::cout << a << "\n";
}

Storing multiple types into class member container

I was reading this Q/A here and as my question is similar but different I would like to know how to do the following:
Let's say I have a basic non template non inherited class called Storage.
class Storage {};
I would like for this class to have a single container (unordered multimap) is where I'm leaning towards... That will hold a std::string for a name id to a variable type T. The class itself will not be template. However a member function to add in elements would be. A member function to add might look like this:
template<T>
void addElement( const std::string& name, T& t );
This function will then populate the unorderd multimap. However each time this function is called each type could be different. So my map would look something like:
"Hotdogs", 8 // here 8 is int
"Price", 4.85f // here 4.8f is float.
How would I declare such an unorderd multimap using templates, variadic parameters, maybe even tuple, any or variant... without the class itself being a template? I prefer not to use boost or other libraries other than the standard.
I tried something like this:
class Storage {
private:
template<class T>
typedef std::unorderd_multimap<std::string, T> DataTypes;
template<class... T>
typedef std::unordered_multimap<std::vector<std::string>, std::tuple<T...>> DataTypes;
};
But I can not seem to get the typedefs correct so that I can declare them like this:
{
DataTypes mDataTypes;
}
You tagged C++17, so you could use std::any (or std::variant if the T type can be a limited and know set of types`).
To store the values is simple.
#include <any>
#include <unordered_map>
class Storage
{
private:
using DataTypes = std::unordered_multimap<std::string, std::any>;
DataTypes mDataTypes;
public:
template <typename T>
void addElement (std::string const & name, T && t)
{ mDataTypes.emplace(name, std::forward<T>(t)); }
};
int main()
{
Storage s;
s.addElement("Hotdogs", 8);
s.addElement("Price", 4.85f);
// but how extract the values ?
}
But the problem is that now you have a element with "Hotdogs" and "Price" keys in the map, but you have no info about the type of the value.
So you have to save, in some way, a info about the type of th value (transform the value in a std::pair with some id-type and the std::any?) to extract it when you need it.
I've done something along those lines, the actual solution is very specific to your problem.
That being said, I'm doing this on a vector, but the principle applies to maps, too.
If you're not building an API and hence know all classes that will be involved you could use std::variant something along the lines of this:
#include <variant>
#include <vector>
#include <iostream>
struct ex1 {};
struct ex2 {};
using storage_t = std::variant<ex1, ex2>;
struct unspecific_operation {
void operator()(ex1 arg) { std::cout << "got ex1\n";}
void operator()(ex2 arg) { std::cout << "got ex2\n";}
};
int main() {
auto storage = std::vector<storage_t>{};
storage.push_back(ex1{});
storage.push_back(ex2{});
auto op = unspecific_operation{};
for(const auto& content : storage) {
std::visit(op, content);
}
return 0;
}
which will output:
got ex1
got ex2
If I remember correctly, using std::any will enable RTTI, which can get quite expensive; might be wrong tho.
If you provide more specifics about what you actually want to do with it, I can give you a more specific solution.
for an example with the unordered map:
#include <variant>
#include <unordered_map>
#include <string>
#include <iostream>
struct ex1 {};
struct ex2 {};
using storage_t = std::variant<ex1, ex2>;
struct unspecific_operation {
void operator()(ex1 arg) { std::cout << "got ex1\n";}
void operator()(ex2 arg) { std::cout << "got ex2\n";}
};
class Storage {
private:
using map_t = std::unordered_multimap<std::string, storage_t>;
map_t data;
public:
Storage() : data{map_t{}}
{}
void addElement(std::string name, storage_t elem) {
data.insert(std::make_pair(name, elem));
}
void doSomething() {
auto op = unspecific_operation{};
for(const auto& content : data) {
std::visit(op, content.second);
}
}
};
int main() {
auto storage = Storage{};
storage.addElement("elem1", ex1{});
storage.addElement("elem2", ex2{});
storage.addElement("elem3", ex1{});
storage.doSomething();
return 0;
}

Resources