If I want to concatenate two matrices A and B, I would do
using Eigen::MatrixXd;
const MatrixXd A(n, p);
const MatrixXd B(n, q);
MatrixXd X(n, p+q);
X << A, B;
Now if n, p, q are large, defining X in this way would mean creating copies of A and B. Is it possible to define X as an Eigen::Ref<MatrixXd> instead?
Thanks.
No, Ref is not designed for that. We/You would need to define a new expression for that, that could be called Cat. If you only need to concatenate two matrices horizontally, in Eigen 3.3, this can be implemented in less than a dozen of lines of code as a nullary expression, see some exemple there.
Edit: here is a self-contained example showing that one can mix matrices and expressions:
#include <iostream>
#include <Eigen/Core>
using namespace Eigen;
template<typename Arg1, typename Arg2>
struct horizcat_helper {
typedef Matrix<typename Arg1::Scalar,
Arg1::RowsAtCompileTime,
Arg1::ColsAtCompileTime==Dynamic || Arg2::ColsAtCompileTime==Dynamic
? Dynamic : Arg1::ColsAtCompileTime+Arg2::ColsAtCompileTime,
ColMajor,
Arg1::MaxRowsAtCompileTime,
Arg1::MaxColsAtCompileTime==Dynamic || Arg2::MaxColsAtCompileTime==Dynamic
? Dynamic : Arg1::MaxColsAtCompileTime+Arg2::MaxColsAtCompileTime> MatrixType;
};
template<typename Arg1, typename Arg2>
class horizcat_functor
{
const typename Arg1::Nested m_mat1;
const typename Arg2::Nested m_mat2;
public:
horizcat_functor(const Arg1& arg1, const Arg2& arg2)
: m_mat1(arg1), m_mat2(arg2)
{}
const typename Arg1::Scalar operator() (Index row, Index col) const {
if (col < m_mat1.cols())
return m_mat1(row,col);
return m_mat2(row, col - m_mat1.cols());
}
};
template <typename Arg1, typename Arg2>
CwiseNullaryOp<horizcat_functor<Arg1,Arg2>, typename horizcat_helper<Arg1,Arg2>::MatrixType>
horizcat(const Eigen::MatrixBase<Arg1>& arg1, const Eigen::MatrixBase<Arg2>& arg2)
{
typedef typename horizcat_helper<Arg1,Arg2>::MatrixType MatrixType;
return MatrixType::NullaryExpr(arg1.rows(), arg1.cols()+arg2.cols(),
horizcat_functor<Arg1,Arg2>(arg1.derived(),arg2.derived()));
}
int main()
{
MatrixXd mat(3, 3);
mat << 0, 1, 2, 3, 4, 5, 6, 7, 8;
auto example1 = horizcat(mat,2*mat);
std::cout << example1 << std::endl;
auto example2 = horizcat(VectorXd::Ones(3),mat);
std::cout << example2 << std::endl;
return 0;
}
I'll add the C++14 version of #ggaels horizcat as an answer. The implementation is a bit sloppy in that it does not consider the Eigen compile-time constants, but in return it's only a two-liner:
auto horizcat = [](auto expr1, auto expr2)
{
auto get = [expr1=std::move(expr1),expr2=std::move(expr2)](auto row, auto col)
{ return col<expr1.cols() ? expr1(row, col) : expr2(row, col - expr1.cols());};
return Eigen::Matrix<decltype(get(0,0)), Eigen::Dynamic, Eigen::Dynamic>::NullaryExpr(expr1.rows(), expr1.cols() + expr2.cols(), get);
};
int main()
{
Eigen::MatrixXd mat(3, 3);
mat << 0, 1, 2, 3, 4, 5, 6, 7, 8;
auto example1 = horizcat(mat,2*mat);
std::cout << example1 << std::endl;
auto example2 = horizcat(Eigen::MatrixXd::Identity(3,3), mat);
std::cout << example2 << std::endl;
return 0;
}
Note that the code is untested.
That should be appropriate for most applications. However, in case you're using compile-time matrix dimensions and require maximum performance, prefer ggaels answer. In all other cases, also prefer ggaels answer, because he is the developer of Eigen :-)
I expanded ggael's answer to Array types, vertical concatenation, and more than two arguments:
#include <iostream>
#include <Eigen/Core>
namespace EigenCustom
{
using namespace Eigen;
constexpr Index dynamicOrSum( const Index& a, const Index& b ){
return a == Dynamic || b == Dynamic ? Dynamic : a + b;
}
enum class Direction { horizontal, vertical };
template<Direction direction, typename Arg1, typename Arg2>
struct ConcatHelper {
static_assert( std::is_same_v<
typename Arg1::Scalar, typename Arg2::Scalar
> );
using Scalar = typename Arg1::Scalar;
using D = Direction;
static constexpr Index
RowsAtCompileTime { direction == D::horizontal ?
Arg1::RowsAtCompileTime :
dynamicOrSum( Arg1::RowsAtCompileTime, Arg2::RowsAtCompileTime )
},
ColsAtCompileTime { direction == D::horizontal ?
dynamicOrSum( Arg1::ColsAtCompileTime, Arg2::ColsAtCompileTime ) :
Arg1::ColsAtCompileTime
},
MaxRowsAtCompileTime { direction == D::horizontal ?
Arg1::MaxRowsAtCompileTime :
dynamicOrSum( Arg1::MaxRowsAtCompileTime, Arg2::MaxRowsAtCompileTime )
},
MaxColsAtCompileTime { direction == D::horizontal ?
dynamicOrSum( Arg1::MaxColsAtCompileTime, Arg2::MaxColsAtCompileTime ) :
Arg1::MaxColsAtCompileTime
};
static_assert(
(std::is_base_of_v<MatrixBase<Arg1>, Arg1> &&
std::is_base_of_v<MatrixBase<Arg2>, Arg2> ) ||
(std::is_base_of_v<ArrayBase<Arg1>, Arg1> &&
std::is_base_of_v<ArrayBase<Arg2>, Arg2> )
);
using DenseType = std::conditional_t<
std::is_base_of_v<MatrixBase<Arg1>, Arg1>,
Matrix<
Scalar, RowsAtCompileTime, ColsAtCompileTime,
ColMajor, MaxRowsAtCompileTime, MaxColsAtCompileTime
>,
Array<
Scalar, RowsAtCompileTime, ColsAtCompileTime,
ColMajor, MaxRowsAtCompileTime, MaxColsAtCompileTime
>
>;
};
template<Direction direction, typename Arg1, typename Arg2>
class ConcatFunctor
{
using Scalar = typename ConcatHelper<direction, Arg1, Arg2>::Scalar;
const typename Arg1::Nested m_mat1;
const typename Arg2::Nested m_mat2;
public:
ConcatFunctor(const Arg1& arg1, const Arg2& arg2)
: m_mat1(arg1), m_mat2(arg2)
{}
const Scalar operator() (Index row, Index col) const {
if constexpr (direction == Direction::horizontal){
if (col < m_mat1.cols())
return m_mat1(row,col);
return m_mat2(row, col - m_mat1.cols());
} else {
if (row < m_mat1.rows())
return m_mat1(row,col);
return m_mat2(row - m_mat1.rows(), col);
}
}
};
template<Direction direction, typename Arg1, typename Arg2>
using ConcatReturnType = CwiseNullaryOp<
ConcatFunctor<direction,Arg1,Arg2>,
typename ConcatHelper<direction,Arg1,Arg2>::DenseType
>;
template<Direction direction, typename Arg1, typename Arg2>
ConcatReturnType<direction, Arg1, Arg2>
concat(
const Eigen::DenseBase<Arg1>& arg1,
const Eigen::DenseBase<Arg2>& arg2
){
using DenseType = typename ConcatHelper<direction,Arg1,Arg2>::DenseType;
using D = Direction;
return DenseType::NullaryExpr(
direction == D::horizontal ? arg1.rows() : arg1.rows() + arg2.rows(),
direction == D::horizontal ? arg1.cols() + arg2.cols() : arg1.cols(),
ConcatFunctor<direction,Arg1,Arg2>( arg1.derived(), arg2.derived() )
);
}
template<Direction direction, typename Arg1, typename Arg2, typename ... Ts>
decltype(auto)
concat(
const Eigen::DenseBase<Arg1>& arg1,
const Eigen::DenseBase<Arg2>& arg2,
Ts&& ... rest
){
return concat<direction>(
concat<direction>(arg1, arg2),
std::forward<Ts>(rest) ...
);
}
template<typename Arg1, typename Arg2, typename ... Ts>
decltype(auto)
concat_horizontal(
const Eigen::DenseBase<Arg1>& arg1,
const Eigen::DenseBase<Arg2>& arg2,
Ts&& ... rest
){
return concat<Direction::horizontal>(
arg1, arg2, std::forward<Ts>(rest) ...
);
}
template<typename Arg1, typename Arg2, typename ... Ts>
decltype(auto)
concat_vertical(
const Eigen::DenseBase<Arg1>& arg1,
const Eigen::DenseBase<Arg2>& arg2,
Ts&& ... rest
){
return concat<Direction::vertical>(
arg1, arg2, std::forward<Ts>(rest) ...
);
}
} // namespace EigenCustom
int main()
{
using namespace Eigen;
using namespace EigenCustom;
MatrixXd mat(3, 3);
mat << 0, 1, 2, 3, 4, 5, 6, 7, 8;
auto example1 = concat_horizontal(mat,2*mat);
std::cout << "example1:\n" << example1 << '\n';
auto example2 = concat_horizontal(VectorXd::Ones(3),mat);
std::cout << "example2:\n" << example2 << '\n';
auto example3 = concat_vertical(mat,RowVectorXd::Zero(3));
std::cout << "example3:\n" << example3 << '\n';
ArrayXXi arr (2,2);
arr << 0, 1, 2, 3;
auto example4 = concat_vertical(arr,Array2i{4,5}.transpose());
std::cout << "example4:\n" << example4 << '\n';
/* concatenating more than two arguments */
auto example5 = concat_horizontal(mat, mat, mat);
std::cout << "example5:\n" << example5 << '\n';
using RowArray2i = Array<int, 1, 2>;
auto example6 = concat_vertical( arr, RowArray2i::Zero(), RowArray2i::Ones() );
std::cout << "example6:\n" << example6 << '\n';
return 0;
}
Related
I have a template class with 3 template arguments.
template <class T, class U, class Y>
class MyClass {};
I wanna get input from users by CLI arguments, something like ./cli float driver-x load
The first arg can be float or double
The second arg is a driver name: driver-x, driver-y, ...
The third argument is about the action type: load, unload, ...
If I want to create a new instance of MyClass based on user inputs, I have to define many if/else statements. Because a user inputs are string and I have to prepare a condition on them.
So, it will be something like this:
if (data_type == "float")
if (driver == "driver-x")
if (action == "load")
MyClass<float, DriverX, Load> t;
t......
As far as I know, it's impossible to store a type in a variable in C++.
So, is there any way exists to improve the if/else statements? Something like:
if (data_type == "float")
//
if (driver == "driver-x")
//
if (action == "load")
//
MyClass<......> t;
t.....;
Or any other way?
I'm looking for a way to improve these if/else statements.
Here's my take
template<typename T>
struct proxy { // or std::type_identity
using type = T;
};
template<typename... Ts>
using choice_of = std::variant<proxy<Ts>...>;
template<typename T, typename>
using type_const_t = T;
template<typename T, typename... Ts>
std::optional<choice_of<T, Ts...>> choose(std::string const &choice, std::string const &head, type_const_t<std::string const&, Ts>... tail) noexcept {
if(choice == head) return proxy<T>{};
else if constexpr(sizeof...(Ts) == 0) return std::nullopt;
else if(auto rec = choose<Ts...>(choice, tail...)) return std::visit(
[](auto rec) -> choice_of<T, Ts...> { return rec; },
*rec);
else return std::nullopt;
}
auto data_choice = choose<float, double>(data_type, "float", "double");
auto driver_choice = choose<DriverX, DriverY>(driver, "driver-x", "driver-y");
auto action_choice = choose<Load, Unload>(action, "load", "unload");
std::visit([](auto data_type_p, auto driver_p, auto action_p) {
auto t = MyClass<typename decltype(data_type_p)::type, typename decltype(driver_p)::type, typename decltype(action_p)::type>{};
// do stuff with t
}, data_choice.value(), driver_choice.value(), action_choice.value());
Complete example on Godbolt
You can build some machinery to do this for you, extracting it into a function call.
For example, here I build a tuple which contains strings and types, then I check a passed string against all of them:
#include <string_view>
#include <cstddef>
#include <tuple>
#include <utility>
#include <type_traits>
template<class T>
struct mapped_type {
const std::string_view key;
using type = T;
explicit constexpr operator bool() const noexcept {
return true;
}
};
namespace detail {
template<class K, class F, class M, std::size_t I>
constexpr void lookup_impl(const K& key, F&& f, M&& m, std::integral_constant<std::size_t, I>) {
using tuple_t = typename std::remove_cv<typename std::remove_reference<M>::type>::type;
if constexpr (I < std::tuple_size<tuple_t>::value) {
const auto& mapping = std::get<I>(m);
if (mapping.key == key) {
std::forward<F>(f)(mapping);
return;
}
lookup_impl(key, std::forward<F>(f), std::forward<M>(m), std::integral_constant<std::size_t, I + 1>{});
} else {
std::forward<F>(f)(std::false_type{});
}
}
}
// Calls `f` with the first value from `m` that matches the key
// or `std::false_type{}` if no key matches.
template<class K, class F, class M>
constexpr void lookup(const K& key, F&& f, M&& m) {
detail::lookup_impl(key, std::forward<F>(f), std::forward<M>(m), std::integral_constant<std::size_t, 0>{});
}
// This is our mapping for the first argument
inline constexpr auto data_type_map = std::make_tuple(
mapped_type<float>{ "float" },
mapped_type<double>{ "double" }
);
// Example usage
#include <iostream>
int main() {
const char* s = "float";
lookup(s, [](const auto& arg) {
if constexpr (!arg) {
std::cout << "Invalid type\n";
} else {
using type = typename std::remove_cv<typename std::remove_reference<decltype(arg)>::type>::type::type;
std::cout << "Got type: " << typeid(type).name() << '\n';
}
}, data_type_map);
}
And then you can call this recursively inside the lambda.
You could also create a version that takes a tuple of keys and a tuple of values to call one function with many arguments:
#include <string_view>
#include <tuple>
#include <utility>
#include <type_traits>
template<class T>
struct mapped_type {
const std::string_view key;
using type = T;
explicit constexpr operator bool() const noexcept {
return true;
}
};
namespace detail {
template<class K, class F, class M, std::size_t I>
constexpr void lookup_impl(F&& f, const K& key, M&& m, std::integral_constant<std::size_t, I>) {
using tuple_t = typename std::remove_cv<typename std::remove_reference<M>::type>::type;
if constexpr (I < std::tuple_size<tuple_t>::value) {
const auto& mapping = std::get<I>(m);
if (mapping.key == key) {
std::forward<F>(f)(mapping);
return;
}
lookup_impl(std::forward<F>(f), key, std::forward<M>(m), std::integral_constant<std::size_t, I + 1>{});
} else {
std::forward<F>(f)(std::false_type{});
}
}
template<class F, class K, class M, std::size_t I>
constexpr void multilookup_impl(F&& f, const K& keys, M&& mappings, std::integral_constant<std::size_t, I>) {
constexpr std::size_t size = std::tuple_size<typename std::remove_cv<typename std::remove_reference<K>::type>::type>::value;
if constexpr (I >= size) {
std::forward<F>(f)();
} else {
lookup_impl([&](const auto& current_lookup) {
multilookup_impl(
[&](const auto&... args) { std::forward<F>(f)(current_lookup, args...); },
keys, mappings, std::integral_constant<std::size_t, I + 1>{}
);
}, std::get<I>(keys), std::get<I>(mappings), std::integral_constant<std::size_t, 0>{});
}
}
}
template<class F, class K, class M>
constexpr void lookup(F&& f, const K& keys, M&& mappings) {
using map_tuple_t = typename std::remove_cv<typename std::remove_reference<M>::type>::type;
using key_tuple_t = typename std::remove_cv<typename std::remove_reference<K>::type>::type;
constexpr std::size_t size = std::tuple_size<key_tuple_t>::value;
static_assert(size == std::tuple_size<map_tuple_t>::value, "Wrong number of keys for given number of maps");
detail::multilookup_impl(std::forward<F>(f), keys, mappings, std::integral_constant<std::size_t, 0>{});
}
Which looks almost the same, but there's one more level of calls.
It would be used like this:
#include <iostream>
inline constexpr auto data_type_map = std::make_tuple(
mapped_type<float>{ "float" },
mapped_type<double>{ "double" }
);
inline constexpr auto driver_type_map = std::make_tuple(
mapped_type<DriverX>{ "driver-x" },
mapped_type<DriverY>{ "driver-y" }
);
inline constexpr auto action_type_map = std::make_tuple(
mapped_type<Load>{ "load" },
mapped_type<Unload>{ "unload" }
);
int main() {
const char* a = "float";
const char* b = "driver-x";
const char* c = "load";
lookup([](const auto& data, const auto& driver, const auto& action) {
if constexpr (!data) {
std::cout << "Could not parse data!\n";
} else if constexpr (!driver) {
std::cout << "Could not parse driver!\n";
} else if constexpr (!action) {
std::cout << "Could not parse action!\n";
} else {
using data_type = typename std::remove_cv<typename std::remove_reference<decltype(data)>::type>::type::type;
using driver_type = typename std::remove_cv<typename std::remove_reference<decltype(driver)>::type>::type::type;
using action_type = typename std::remove_cv<typename std::remove_reference<decltype(action)>::type>::type::type;
MyClass<data_type, driver_type, action_type> t;
std::cout << "Constructed a " << typeid(decltype(t)).name() << '\n';
}
},
std::array<const char*, 3>{ a, b, c },
std::forward_as_tuple(data_type_map, driver_type_map, action_type_map)
);
}
I think you are looking for something like X-macros:
#define YOUR_TABLE \
X(float, DriverX, "driver-x", Load) \
X(int, DriverY, "driver-y", action2) \
X(int, DriverY, "driver-y", action3)
#define X(data_type, driver, driverName, action) if((0 == strcmp(#data_type,argv[1])) \
&& (0 == strcmp(driverName,argv[2])) && (0 == strcmp(#action,argv[3])))\
{ \
MyClass<data_type, driver, action> t; \
t.... \
}
YOUR_TABLE
#undef X
Prepare your puke-bag, here is a far-from-elegant solution but
simple enough to be easily adapted.
The main drawback I see is that all the remaining of the application
that needs to work with the created instance must stand in a
lambda-closure (this solution does not return this instance).
Every possible argument is considered only once in a
dedicated function (not X times Y times Z if/else).
/**
g++ -std=c++17 -o prog_cpp prog_cpp.cpp \
-pedantic -Wall -Wextra -Wconversion -Wno-sign-conversion \
-g -O0 -UNDEBUG -fsanitize=address,undefined
**/
#include <iostream>
#include <string>
#include <stdexcept>
//----------------------------------------------------------------------------
struct DriverX { auto show() const { return "DriverX"; } };
struct DriverY { auto show() const { return "DriverY"; } };
struct Load { auto show() const { return "Load"; } };
struct Unload { auto show() const { return "UnLoad"; } };
template<typename RealType,
typename DriverType,
typename ActionType>
struct MyClass
{
RealType real{};
DriverType driver{};
ActionType action{};
auto show() const
{
return std::to_string(sizeof(real))+" bytes real, "+
driver.show()+", "+action.show();
}
};
//----------------------------------------------------------------------------
template<typename RealType,
typename DriverType,
typename DoEverythingFunction>
void
with_MyClass_3(const std::string &action,
DoEverythingFunction fnct)
{
if(action=="load")
{
return fnct(MyClass<RealType, DriverType, Load>{});
}
if(action=="unload")
{
return fnct(MyClass<RealType, DriverType, Unload>{});
}
throw std::runtime_error{"unexpected action: "+action};
}
template<typename RealType,
typename DoEverythingFunction>
void
with_MyClass_2(const std::string &driver,
const std::string &action,
DoEverythingFunction fnct)
{
if(driver=="driver-x")
{
return with_MyClass_3<RealType, DriverX>(action, fnct);
}
if(driver=="driver-y")
{
return with_MyClass_3<RealType, DriverY>(action, fnct);
}
throw std::runtime_error{"unexpected driver: "+driver};
}
template<typename DoEverythingFunction>
void
with_MyClass(const std::string &real,
const std::string &driver,
const std::string &action,
DoEverythingFunction fnct)
{
if(real=="float")
{
return with_MyClass_2<float>(driver, action, fnct);
}
if(real=="double")
{
return with_MyClass_2<double>(driver, action, fnct);
}
throw std::runtime_error{"unexpected real: "+real};
}
//----------------------------------------------------------------------------
int
main(int argc,
char **argv)
{
std::cout << "~~~~ hardcoded types ~~~~\n";
const MyClass<float, DriverX, Load> mc1;
std::cout << "mc1: " << mc1.show() << '\n';
const MyClass<double, DriverY, Unload> mc2;
std::cout << "mc2: " << mc2.show() << '\n';
std::cout << "\n~~~~ many types ~~~~\n";
for(const auto &real: {"float", "double", "int"})
{
for(const auto &driver: {"driver-x", "driver-y", "driver-z"})
{
for(const auto &action: {"load", "unload", "sleep"})
{
try
{
with_MyClass(real, driver, action,
[&](const auto &mc)
{
std::cout << "working with: " << mc.show() << '\n';
});
}
catch(const std::exception &e)
{
std::cerr << "!!! " << e.what() << " !!!\n";
}
}
}
}
if(argc>3)
{
std::cout << "\n~~~~ from command line ~~~~\n";
try
{
with_MyClass(argv[1], argv[2], argv[3],
[&](const auto &mc)
{
std::cout << "working with: " << mc.show() << '\n';
});
}
catch(const std::exception &e)
{
std::cerr << "!!! " << e.what() << " !!!\n";
}
}
return 0;
}
I have this sample code, which do what I need for a 3-parameter function :
template<typename T>T GETPARAM(void) { return T(); }
template<>int GETPARAM(void) { return 123; }
template<>double GETPARAM(void) { return 1.2345; }
template<>const char *GETPARAM(void) { return "hello"; }
template<typename P1, typename P2, typename P3, typename RES> RES BuildArgs3(RES(*fn)(P1, P2, P3)) {
P1 p1 = GETPARAM<P1>();
P2 p2 = GETPARAM<P2>();
P3 p3 = GETPARAM<P3>();
return fn(p1, p2, p3);
}
int print3(int a, double b, const char *c)
{
Cout() << "Print3:" << a << ", " << b << ", " << c << "\n";
return 1;
}
main() {
BuildArgs3(print3);
}
(the GETPARAM templates are there just to show the call).
I tried to generalize it with a variadic template for functions with any number of arguments with no success. Is it possible ?
The template shall be useable for any T (*fn)(P1, P2, ...) with any return type and any number of parameters, building the parameters on the fly calling the GETPARAM<Pn>() for each of them.
It's needed to create a binding system for a scripting language, fetching parameters from a stack and calling a C++ function when done.
I tried to generalize it with a variadic template for functions with any number of arguments with no success. Is it possible ?
Yes; and it's simple
template <typename R, typename ... Args>
R BuildArgsN (R(*fn)(Args...))
{ return fn(GETPARAM<Args>()...); }
The following is a full working example
#include <iostream>
template<typename T>T GETPARAM(void) { return T(); }
template<>int GETPARAM(void) { return 123; }
template<>double GETPARAM(void) { return 1.2345; }
template<>const char *GETPARAM(void) { return "hello"; }
template <typename R, typename ... Args>
R BuildArgsN (R(*fn)(Args...))
{ return fn(GETPARAM<Args>()...); }
int print3 (int a, double b, char const * c)
{
std::cout << "Print3:" << a << ", " << b << ", " << c << "\n";
return 1;
}
int main ()
{
BuildArgsN(print3);
}
If the calls to GETPARAM should be ordered then you have to expand the variadic pack in a context that guarantees a particular order. One option is list initialization:
Every initializer clause is sequenced before any initializer clause
that follows it in the braced-init-list. This is in contrast with the
arguments of a function call expression, which are unsequenced.
Let us consider your given example: You can expand the argument pack yielding the GETPARAM calls inside the curly braces constructing a proxy object. The proxy object can be implicitly convertible to the return type of your function.
#include <iostream>
int pos = 0;// DEBUG: observe the order of `GETPARAM` calls
template<typename T>T GETPARAM();
template<>
int GETPARAM() { return 100 + pos++; }
template<>
double GETPARAM() { return 100.5 + pos++; }
template<>
const char* GETPARAM() { pos++; return "hello"; }
////////////////////////////////////////////////////////////////////////////////
template<class Ret>
struct ArgEvalOrderer {
Ret ret;
template<class... Args>
ArgEvalOrderer(
Ret(*f)(Args...),
Args... args
)
: ret{f(args...)}
{}
operator Ret() const { return ret; }
};
template<class Ret, class... Args>
Ret call_after_ordered_argfetch(Ret(*f)(Args...)) {
// evaluation order guaranteed by braced init list
return ArgEvalOrderer<Ret>{f, GETPARAM<Args>()...};
}
template<class Ret, class... Args>
Ret call_after_ordered_argfetch_buggy(Ret(*f)(Args...)) {
// BUGGY: NO GUARANTEE on evaluation order
return ArgEvalOrderer<Ret>(f, GETPARAM<Args>()...);
}
template<class Ret, class... Args>
Ret call_after_unordered_argfetch(Ret(*f)(Args...)) {
// BUGGY: NO GUARANTEE on evaluation order
return f(GETPARAM<Args>()...);
}
int print7(int a, int b, double c, int d, double e, const char* f, double g) {
std::cout << "print7: " << a
<< ", " << b
<< ", " << c
<< ", " << d
<< ", " << e
<< ", " << f
<< ", " << g
<< std::endl;
return 1;
}
int main() {
call_after_ordered_argfetch(print7);
call_after_ordered_argfetch_buggy(print7);
call_after_unordered_argfetch(print7);
return 0;
}
Note that the upper version is the only one which guarantees ordered evaluation. In fact, I observe (online demo) the following output:
g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
print7: 100, 101, 102.5, 103, 104.5, hello, 106.5
print7: 113, 112, 111.5, 110, 109.5, hello, 107.5
print7: 120, 119, 118.5, 117, 116.5, hello, 114.5
Here is a code for statically building any function with any number of arguments. It Is completely independent from any compiler as gcc and libraries as std or stl (excepting the printf's used, but you can remove them safely). But requires c++0x because of the variadic templates.
#include <stdio.h>
// SIZEOF Type Package
template<typename ... Tn>
struct SIZEOF
{ static const unsigned int Result = 0; };
template<typename T1, typename ... Tn>
struct SIZEOF<T1, Tn ...>
{ static const unsigned int Result = sizeof(T1) + SIZEOF<Tn ...>::Result ; };
template<int ...>
struct MetaSequenceOfIntegers { };
template<int AccumulatedSize, typename Tn, int... GeneratedSequence>
struct GeneratorOfIntegerSequence;
template<
int AccumulatedSize,
typename Grouper,
typename Head,
typename... Tail,
int... GeneratedSequence
>
struct GeneratorOfIntegerSequence<
AccumulatedSize, Grouper( Head, Tail... ), GeneratedSequence... >
{
typedef typename GeneratorOfIntegerSequence
<
AccumulatedSize + sizeof(Head),
Grouper( Tail... ),
GeneratedSequence...,
AccumulatedSize
>::type type;
};
template<int AccumulatedSize, typename Grouper, int... GeneratedSequence>
struct GeneratorOfIntegerSequence<AccumulatedSize, Grouper(), GeneratedSequence...>
{
typedef MetaSequenceOfIntegers<GeneratedSequence...> type;
};
template<typename Tn>
class Closure;
template<typename ReturnType, typename... Tn>
class Closure<ReturnType( Tn... )>
{
public:
typedef ReturnType(*Function)(Tn ...);
static const unsigned int PARAMETERS_COUNT = sizeof...( Tn );
static const unsigned int PARAMETERS_LENGTH = SIZEOF<Tn ...>::Result;
private:
Function _entry;
char* _parameters;
public:
Closure(Function _entry, Tn ... an): _entry(_entry)
{
printf( "Closure::Closure(_entry=%d, PARAMETERS_COUNT=%d,
PARAMETERS_LENGTH=%d, sizeof=%d) => %d\n",
&_entry, PARAMETERS_COUNT, PARAMETERS_LENGTH, sizeof(*this), this );
if(PARAMETERS_LENGTH) _parameters = new char[PARAMETERS_LENGTH];
pack_helper( _parameters, an ... );
}
~Closure() {
printf( "Closure::~Closure(this=%d, _entry=%d,
PARAMETERS_COUNT=%d, PARAMETERS_LENGTH=%d, sizeof=%d)\n",
this, &_entry, PARAMETERS_COUNT, PARAMETERS_LENGTH, sizeof(*this) );
if(PARAMETERS_LENGTH) delete _parameters;
}
ReturnType operator()() {
return _run( typename GeneratorOfIntegerSequence< 0, int(Tn...) >::type() );
}
private:
template<int ...Sequence>
ReturnType _run(MetaSequenceOfIntegers<Sequence...>)
{
printf( "Closure::_run(this=%d)\n", this );
return _entry( unpack_helper<Sequence, Tn>()... );
}
template<const int position, typename T>
T unpack_helper()
{
printf( "Closure::unpack_helper(Head=%d, address=%d(%d), position=%d)\n",
sizeof( T ), _parameters + position, _parameters, position );
return *reinterpret_cast<T *>( _parameters + position );
}
public:
template<typename Head, typename ... Tail>
static void pack_helper(char* pointer_address, Head head, Tail ... tail)
{
printf( "Closure::pack_helper(
Head=%d, address=%d)\n", sizeof( Head ), pointer_address );
*reinterpret_cast<Head *>(pointer_address) = head;
pack_helper(pointer_address + sizeof( Head ), tail ...);
}
static void pack_helper(char* pointer_address) {}
};
/**
* Create a closure which can have any return type.
*/
template<typename ReturnType, typename ... Tn>
Closure< ReturnType(Tn ...) > create_closure(
ReturnType(*_entry)( Tn ... ), Tn ... an )
{
auto closure = new Closure< ReturnType(Tn ...) >( _entry, an ... );
printf( "create_closure=%d\n", closure );
return *closure;
}
char test_function1(char arg1, int arg2, bool arg3) {
printf(" test_function1: %c, %d, %d\n", arg1, arg2, arg3);
}
char test_function2(const char* arg1, const char* arg2, char arg3) {
printf(" test_function2: %s, %s, %c\n", arg1, arg2, arg3);
}
char test_function3() {
printf(" test_function3\n");
}
void test_function4() {
printf(" test_function4\n");
}
void test_function5(const char* arg1) {
printf(" test_function5=%s\n", arg1);
}
template<typename ... Tn>
void test_closure(Tn ... an) {
auto closure = create_closure(an ...);
closure();
printf( "\n" );
}
// clang++ -Xclang -ast-print -fsyntax-only test.cpp > expanded.cpp
int main()
{
test_closure( &test_function1, 'a', 10, false );
test_closure( &test_function2, "test1", "test2", 'b' );
test_closure( &test_function3 );
test_closure( &test_function4 );
test_closure( &test_function5, "Testa 3" );
test_closure( &test_function5, "Testa 4" );
}
Running it you will see the tests results:
$ g++ -o test test_variadic_critical_section_dynamic.cpp && ./test
Closure::Closure(_entry=-13672,
PARAMETERS_COUNT=3, PARAMETERS_LENGTH=6, sizeof=16) => 164864
Closure::pack_helper(Head=1, address=164976)
Closure::pack_helper(Head=4, address=164977)
Closure::pack_helper(Head=1, address=164981)
create_closure=164864
Closure::_run(this=-13520)
Closure::unpack_helper(Head=1, address=164981(164976), position=5)
Closure::unpack_helper(Head=4, address=164977(164976), position=1)
Closure::unpack_helper(Head=1, address=164976(164976), position=0)
test_function1: a, 10, 0
Closure::~Closure(this=-13520, _entry=-13520,
PARAMETERS_COUNT=3, PARAMETERS_LENGTH=6, sizeof=16)
Closure::Closure(_entry=-13672,
PARAMETERS_COUNT=3, PARAMETERS_LENGTH=17, sizeof=16) => 164976
Closure::pack_helper(Head=8, address=165008)
Closure::pack_helper(Head=8, address=165016)
Closure::pack_helper(Head=1, address=165024)
create_closure=164976
Closure::_run(this=-13520)
Closure::unpack_helper(Head=1, address=165024(165008), position=16)
Closure::unpack_helper(Head=8, address=165016(165008), position=8)
Closure::unpack_helper(Head=8, address=165008(165008), position=0)
test_function2: test1, test2, b
Closure::~Closure(this=-13520, _entry=-13520,
PARAMETERS_COUNT=3, PARAMETERS_LENGTH=17, sizeof=16)
Closure::Closure(_entry=-13624,
PARAMETERS_COUNT=0, PARAMETERS_LENGTH=0, sizeof=16) => 165008
create_closure=165008
Closure::_run(this=-13520)
test_function3
Closure::~Closure(this=-13520, _entry=-13520,
PARAMETERS_COUNT=0, PARAMETERS_LENGTH=0, sizeof=16)
Closure::Closure(_entry=-13624,
PARAMETERS_COUNT=0, PARAMETERS_LENGTH=0, sizeof=16) => 165040
create_closure=165040
Closure::_run(this=-13520)
test_function4
Closure::~Closure(this=-13520, _entry=-13520,
PARAMETERS_COUNT=0, PARAMETERS_LENGTH=0, sizeof=16)
Closure::Closure(_entry=-13624,
PARAMETERS_COUNT=1, PARAMETERS_LENGTH=8, sizeof=16) => 165072
Closure::pack_helper(Head=8, address=609568)
create_closure=165072
Closure::_run(this=-13520)
Closure::unpack_helper(Head=8, address=609568(609568), position=0)
test_function5=Testa 3
Closure::~Closure(this=-13520, _entry=-13520,
PARAMETERS_COUNT=1, PARAMETERS_LENGTH=8, sizeof=16)
Closure::Closure(_entry=-13624,
PARAMETERS_COUNT=1, PARAMETERS_LENGTH=8, sizeof=16) => 609568
Closure::pack_helper(Head=8, address=609600)
create_closure=609568
Closure::_run(this=-13520)
Closure::unpack_helper(Head=8, address=609600(609600), position=0)
test_function5=Testa 4
Closure::~Closure(this=-13520, _entry=-13520,
PARAMETERS_COUNT=1, PARAMETERS_LENGTH=8, sizeof=16)
You can run it with clang++ to see the generated template code:
$ clang++ -Xclang -ast-print -fsyntax-only test.cpp > expanded.cpp
// ...
private:
template<> char _run<<0, 8, 16>>(MetaSequenceOfIntegers<0, 8, 16>)
{
return this->_entry(
this->unpack_helper<0, const char *>(),
this->unpack_helper<8, const char *>(),
this->unpack_helper<16, char>()
);
}
template<> const char *unpack_helper<0, const char *>()
{
return *reinterpret_cast<const char **>(this->_parameters + 0);
}
template<> const char *unpack_helper<8, const char *>() {
return *reinterpret_cast<const char **>(this->_parameters + 8);
}
template<> char unpack_helper<16, char>() {
return *reinterpret_cast<char *>(this->_parameters + 16);
}
// ...
References
How to reverse an integer parameter pack?
Can we see the template instantiated code by C++ compiler
Variadic templates, parameter pack and its discussed ambiguity in a parameter list
"unpacking" a tuple to call a matching function pointer
Not sure if it's possible for more later version of c++. (I can't figure out using traditional c++ to achieve the following behaviofgr.)
For example,
If I have an array defined like this:
In the header file
struct Def {
static const int N = 5;
static const double data[N];
};
In its cpp
const double Def::data[Def::N] = {0,1,2,3,4};
Is it possible to have a template get_subarray such that
get_subarray<Def,2,0>::data will be an array of content {0,2,4}
get_subarray<Def,2,1>::data will be an array of content {1,3}
where
template<typename T, int M, int m>
struct get_phase {
// some code for the array variable data which will
// extract element from T::data for every M sample offset by index m
};
As mentioned in the comments, the OP is interested also in C++14 based solutions.
Here is one of them:
#include<functional>
#include<cstddef>
#include<utility>
#include<array>
template<std::size_t O, typename T, std::size_t N, std::size_t... I>
constexpr std::array<T, sizeof...(I)>
f(const std::array<T, N> &arr, std::index_sequence<I...>) {
return { std::get<I+O>(arr)... };
}
template<std::size_t B, std::size_t E, typename T, std::size_t N>
constexpr auto f(const std::array<T, N> &arr) {
return f<B>(arr, std::make_index_sequence<E-B>());
}
int main() {
constexpr std::array<int, 3> a1 = { 0, 1, 2 };
constexpr auto a2 = f<1, 2>(a1);
static_assert(a1[1] == a2[0], "!");
}
In this case, a2 is equal to { 1 }.
It's worth it checking B and E so as to verify that E is greater than B, but the example should give an idea of what's the way to do it.
To port it to C++11:
Do not use auto as return type, but explicitly specify std::array (easy)
Search on the web one of the available C++11 implementations of integer_sequence and make_index_sequence and use it
If it's fine to be explicit about the indexes and not use a range, here is a naïve snippet that should work in C++11:
#include<cstddef>
#include<utility>
#include<array>
template<std::size_t... I, typename T, std::size_t N>
constexpr std::array<T, sizeof...(I)>
f(const std::array<T, N> &arr) {
return { std::get<I>(arr)... };
}
int main() {
constexpr std::array<int, 3> a1 = { 0, 1, 2 };
constexpr auto a2 = f<1>(a1);
static_assert(a1[1] == a2[0], "!");
}
As in the previous example, a2 is { 1 }.
I like the skypjack's solution but it doesn't extract the requested values. skypjack's version has two parameters, "begin" and "end". The OP requested "stride" or "frequency" and "begin".
I've modified it to match the OP's requested "frequency" and "start" arguments, giving the template non-type parameters more self-explaining names, and rewriting a couple of index calculations:
#include<utility>
#include<iostream>
#include<array>
template <std::size_t Freq, std::size_t Start, typename T, std::size_t Dim,
std::size_t... I>
constexpr std::array<T, sizeof...(I)>
extractHelper (const std::array<T, Dim> & arr,
std::integer_sequence<std::size_t, I...>)
{ return { { std::get<Freq*I+Start>(arr)... } }; }
template <std::size_t Freq, std::size_t Start, typename T, std::size_t Dim>
constexpr auto extractSamples (const std::array<T, Dim> & arr)
{ return extractHelper<Freq, Start>
(arr, std::make_index_sequence<(Dim+Freq-1-Start)/Freq>()); }
Here is some test code:
int main()
{
constexpr std::array<int, 8> a1 = { { 0, 1, 2, 3, 4, 5, 6, 7 } };
constexpr auto e1 = extractSamples<2, 0>(a1);
constexpr auto e2 = extractSamples<2, 1>(a1);
constexpr auto e3 = extractSamples<3, 0>(a1);
constexpr auto e4 = extractSamples<3, 1>(a1);
constexpr auto e5 = extractSamples<3, 2>(a1);
std::cout << "samples<2, 0>: ";
for ( auto const & i : e1 )
std::cout << ' ' << i;
std::cout << "\nsamples<2, 1>: ";
for ( auto const & i : e2 )
std::cout << ' ' << i;
std::cout << "\nsamples<3, 0>: ";
for ( auto const & i : e3 )
std::cout << ' ' << i;
std::cout << "\nsamples<3, 1>: ";
for ( auto const & i : e4 )
std::cout << ' ' << i;
std::cout << "\nsamples<3, 2>: ";
for ( auto const & i : e5 )
std::cout << ' ' << i;
std::cout << std::endl;
return 0;
}
The output is:
samples<2, 0>: 0 2 4 6
samples<2, 1>: 1 3 5 7
samples<3, 0>: 0 3 6
samples<3, 1>: 1 4 7
samples<3, 2>: 2 5
which matches the OP's requests
Here's a solution in C++11 without additives. As usual,
compiletime recursion makes do for the lack of C++14's std::index_sequence
- in this case by recursively assembling the list of indices that
select the desired sample of the data array.
Given some:
constexpr std::array<T,N> data{{...}};
initialized ... with the Ts of your choice, then:
constexpr auto sample = get_sample<Stride,Offset>(data);
will define sample as a compiletime
std::array<T,M>
populated with the M elements of data obtained by selecting the element
at offset Offset from the start of successive Stride-sized intervals of
data.
#include <array>
#include <type_traits>
constexpr std::size_t
sample_size(std::size_t size, std::size_t stride, std::size_t off)
{
return stride == 0 ? 0 : ((size - off) / stride) +
(off + (((size - off) / stride) * stride) < size);
}
template<
std::size_t Stride = 1, std::size_t Off = 0,
typename T, std::size_t Size, std::size_t ...Is
>
constexpr typename std::enable_if<
sizeof ...(Is) == sample_size(Size,Stride,Off),
std::array<T, sample_size(Size,Stride,Off)>
>::type
get_sample(std::array<T,Size> const & data)
{
return std::array<T,sample_size(Size,Stride,Off)>{{data[Is]... }};
}
template<
std::size_t Stride = 1, std::size_t Off = 0,
typename T, std::size_t Size, std::size_t ...Is
>
constexpr typename std::enable_if<
sizeof ...(Is) != sample_size(Size,Stride,Off),
std::array<T, sample_size(Size,Stride,Off)>
>::type
get_sample(std::array<T,Size> const & data)
{
return
get_sample<Stride,Off,T,Size,Is...,(sizeof...(Is) * Stride) + Off>
(data);
}
By default Stride is 1 and Off is 0. The helper function sample_size
embeds the convention that if Stride is 0 you get an empty sample.
For an illustrative program, you can append:
constexpr std::array<int,5> data1{{0,1,2,3,4}};
constexpr auto sample1 = get_sample(data1);
constexpr auto sample2 = get_sample<2>(data1);
constexpr auto sample3 = get_sample<2,1>(data1);
constexpr auto sample4 = get_sample<6>(data1);
constexpr auto sample5 = get_sample<6,5>(data1);
static_assert(sample5.size() == 0,"");
constexpr std::array<float,6> data2{{1.1,2.2,3.3,4.4,5.5,6.6}};
constexpr auto sample6 = get_sample<2>(data2);
constexpr auto sample7 = get_sample<2,3>(data2);
constexpr auto sample8 = get_sample<3,2>(data2);
constexpr std::array<int,0> data3{};
constexpr auto sample9 = get_sample<0>(data3);
static_assert(sample9.size() == 0,"");
constexpr auto sample10 = get_sample<2>(data3);
static_assert(sample10.size() == 0,"");
#include <iostream>
int main()
{
std::cout << "get_sample<> of {0,1,2,3,4}\n";
for (auto const & e : sample1) {
std::cout << e << ' ';
}
std::cout << '\n';
std::cout << "get_sample<2> of {0,1,2,3,4}\n";
for (auto const & e : sample2) {
std::cout << e << ' ';
}
std::cout << '\n';
std::cout << "get_sample<2,1> of {0,1,2,3,4}\n";
for (auto const & e : sample3) {
std::cout << e << ' ';
}
std::cout << '\n';
std::cout << "get_sample<6> of {0,1,2,3,4}\n";
for (auto const & e : sample4) {
std::cout << e << ' ';
}
std::cout << '\n';
std::cout << "get_sample<2> of {{1.1,2.2,3.3,4.4,5.5,6.6}}\n";
for (auto const & e : sample6) {
std::cout << e << ' ';
}
std::cout << '\n';
std::cout << "get_sample<2,3> of {{1.1,2.2,3.3,4.4,5.5,6.6}}\n";
for (auto const & e : sample7) {
std::cout << e << ' ';
}
std::cout << '\n';
std::cout << "get_sample<3,2> of {{1.1,2.2,3.3,4.4,5.5,6.6}}\n";
for (auto const & e : sample8) {
std::cout << e << ' ';
}
std::cout << '\n';
return 0;
}
which reports:
get_sample<> of {0,1,2,3,4}
0 1 2 3 4
get_sample<2> of {0,1,2,3,4}
0 2 4
get_sample<2,1> of {0,1,2,3,4}
1 3
get_sample<6> of {0,1,2,3,4}
0
get_sample<2> of {1.1,2.2,3.3,4.4,5.5,6.6}
1.1 3.3 5.5
get_sample<2,3> of {1.1,2.2,3.3,4.4,5.5,6.6}
4.4 6.6
get_sample<3,2> of {1.1,2.2,3.3,4.4,5.5,6.6}
3.3 6.6
See it live
If you want to apply this to your class Def, you would redefine it in an amenable
way like:
struct Def {
static constexpr int N = 5;
static constexpr std::array<double,N> data{{0,1,2,3,4}};
};
and get your compiletime sample like:
constexpr auto s = get_sample<2,1>(Def::data);
(g++ 6.1/clang++ 3.8, -std=c++11 -Wall -Wextra -pedantic)
I would like to use variadic templates to help solve an issue using va-args. Basically, I want to call a singular function, pass into the function a "command" along with a variable list of arguments, then dispatch the arguments to another function.
I've implemented this using tried and true (but not type safe) va_list. Here's an attempt I made at doing this using variadic templates. The example doesn't compile below as you will quickly find out why...
#include <iostream>
using namespace std;
typedef enum cmd_t
{
CMD_ZERO,
CMD_ONE,
CMD_TWO,
} COMMANDS;
int cmd0(double a, double b, double c)
{
cout << "cmd0 " << a << ", " << b << ", " << c << endl;
return 0;
}
int cmd1(int a, int b, int c)
{
cout << "cmd1 " << a << ", " << b << ", " << c << endl;
return 1;
}
template<typename... Args>
int DispatchCommand(COMMANDS cmd, Args... args)
{
int stat = 0;
switch (cmd)
{
case CMD_ZERO:
cmd0(args...);
break;
case CMD_ONE:
cmd1(args...);
break;
default:
stat = -1;
break;
}
return stat;
}
int main()
{
int stat;
stat = DispatchCommand(CMD_ZERO, 1, 3.141, 4);
stat = DispatchCommand(CMD_ONE, 5, 6, 7);
stat = DispatchCommand(CMD_TWO, 5, 6, 7, 8, 9);
system("pause");
return 0;
}
Does anyone have an idea on how I can modify this function to use variadic templates correctly?
Write some code that, given a function pointer and a set of arguments, calls it with the longest prefix of those arguments that works.
template<class...>struct types{using type=types;};
template<class types0, size_t N, class types1=types<>>
struct split;
template<class t00, class...t0s, size_t N, class...t1s>
struct split<types<t00,t0s...>,N,types<t1s...>>:
split<types<t0s...>,N-1,types<t1s...,t00>>
{};
template<class...t0s, class...t1s>
struct split<types<t0s...>,0,types<t1s...>>
{
using right=types<t0s...>;
using left=types<t1s...>;
};
template<class>using void_t=void;
template<class Sig,class=void>
struct valid_call:std::false_type{};
template<class F, class...Args>
struct valid_call<F(Args...), void_t<
decltype( std::declval<F>()(std::declval<Args>()...) )
>>:std::true_type {};
template<class R, class types>
struct prefix_call;
template<class R, class...Args>
struct prefix_call<R, types<Args...>> {
template<class F, class... Extra>
std::enable_if_t< valid_call<F(Args...)>::value, R >
operator()(R default, F f, Args&&...args, Extra&&...) const
{
return std::forward<F>(f)(args...);
}
template<class F, class... Extra>
std::enable_if_t< !valid_call<F(Args...)>::value, R >
operator()(R default, F f, Args&&...args, Extra&&...) const
{
return prefix_call<R, typename split<types<Args...>, sizeof...(Args)-1>::left>{}(
std::forward<R>(default), std::forward<F>(f), std::forward<Args>(args)...
);
}
};
template<class R>
struct prefix_call<R, types<>> {
template<class F, class... Extra>
std::enable_if_t< valid_call<F()>::value, R >
operator()(R default, F f, Extra&&...) const
{
return std::forward<F>(f)();
}
template<class F, class... Extra>
std::enable_if_t< !valid_call<F()>::value, R >
operator()(R default, F f, Extra&&...) const
{
return std::forward<R>(default);
}
};
the above code may contain typos.
template<typename... Args>
int DispatchCommand(COMMANDS cmd, Args... args)
{
int stat = 0;
switch (cmd) {
case CMD_ZERO: {
stat = prefix_call<int, Args...>{}(-1, cmd0, std::forward<Args>(args)...);
} break;
case CMD_ONE: {
stat = prefix_call<int, Args...>{}(-1, cmd1, std::forward<Args>(args)...);
} break;
default: {
stat = -1;
} break;
}
return stat;
}
If cmd0 or cmd1 is overridden, you'll have to use the overload set technique.
You may use the following:
template <COMMANDS cmd> struct command
{
template <typename ... Args>
int operator() (Args&&...) const { return -1; }
};
template <> struct command<CMD_ZERO>
{
int operator()(double a, double b, double c) const
{
std::cout << "cmd0 " << a << ", " << b << ", " << c << std::endl;
return 0;
}
};
template <> struct command<CMD_ONE>
{
int operator()(int a, int b, int c) const
{
std::cout << "cmd1 " << a << ", " << b << ", " << c << std::endl;
return 1;
}
};
template <COMMANDS cmd, typename... Args> int DispatchCommand(Args&&... args)
{
return command<cmd>()(std::forward<Args>(args)...);
}
And then use it like:
DispatchCommand<CMD_ZERO>(1., 3.141, 4.);
DispatchCommand<CMD_ONE>(5, 6, 7);
DispatchCommand<CMD_TWO>(5, 6, 7, 8, 9);
Live example
But using directly the different functions seems simpler:
cmd0(1., 3.141, 4.);
cmd1(5, 6, 7);
Is there an easy way to get a slice of an array in C++?
I.e., I've got
array<double, 10> arr10;
and want to get array consisting of five first elements of arr10:
array<double, 5> arr5 = arr10.???
(other than populating it by iterating through first array)
The constructors for std::array are implicitly defined so you can't initialize it with a another container or a range from iterators. The closest you can get is to create a helper function that takes care of the copying during construction. This allows for single phase initialization which is what I believe you're trying to achieve.
template<class X, class Y>
X CopyArray(const Y& src, const size_t size)
{
X dst;
std::copy(src.begin(), src.begin() + size, dst.begin());
return dst;
}
std::array<int, 5> arr5 = CopyArray<decltype(arr5)>(arr10, 5);
You can also use something like std::copy or iterate through the copy yourself.
std::copy(arr10.begin(), arr10.begin() + 5, arr5.begin());
Sure. Wrote this:
template<int...> struct seq {};
template<typename seq> struct seq_len;
template<int s0,int...s>
struct seq_len<seq<s0,s...>>:
std::integral_constant<std::size_t,seq_len<seq<s...>>::value> {};
template<>
struct seq_len<seq<>>:std::integral_constant<std::size_t,0> {};
template<int Min, int Max, int... s>
struct make_seq: make_seq<Min, Max-1, Max-1, s...> {};
template<int Min, int... s>
struct make_seq<Min, Min, s...> {
typedef seq<s...> type;
};
template<int Max, int Min=0>
using MakeSeq = typename make_seq<Min,Max>::type;
template<std::size_t src, typename T, int... indexes>
std::array<T, sizeof...(indexes)> get_elements( seq<indexes...>, std::array<T, src > const& inp ) {
return { inp[indexes]... };
}
template<int len, typename T, std::size_t src>
auto first_elements( std::array<T, src > const& inp )
-> decltype( get_elements( MakeSeq<len>{}, inp ) )
{
return get_elements( MakeSeq<len>{}, inp );
}
Where the compile time indexes... does the remapping, and MakeSeq makes a seq from 0 to n-1.
Live example.
This supports both an arbitrary set of indexes (via get_elements) and the first n (via first_elements).
Use:
std::array< int, 10 > arr = {0,1,2,3,4,5,6,7,8,9};
std::array< int, 6 > slice = get_elements(arr, seq<2,0,7,3,1,0>() );
std::array< int, 5 > start = first_elements<5>(arr);
which avoids all loops, either explicit or implicit.
2018 update, if all you need is first_elements:
Less boilerplaty solution using C++14 (building up on Yakk's pre-14 answer and stealing from "unpacking" a tuple to call a matching function pointer)
template < std::size_t src, typename T, int... I >
std::array< T, sizeof...(I) > get_elements(std::index_sequence< I... >, std::array< T, src > const& inp)
{
return { inp[I]... };
}
template < int N, typename T, std::size_t src >
auto first_elements(std::array<T, src > const& inp)
-> decltype(get_elements(std::make_index_sequence<N>{}, inp))
{
return get_elements(std::make_index_sequence<N>{}, inp);
}
Still cannot explain why this works, but it does (for me on Visual Studio 2017).
This answer might be late... but I was just toying around with slices - so here is my little home brew of std::array slices.
Of course, this comes with a few restrictions and is not ultimately general:
The source array from which a slice is taken must not go out of scope. We store a reference to the source.
I was looking for constant array slices first and did not try to expand this code to both const and non const slices.
But one nice feature of the code below is, that you can take slices of slices...
// ParCompDevConsole.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include "pch.h"
#include <cstdint>
#include <iostream>
#include <array>
#include <stdexcept>
#include <sstream>
#include <functional>
template <class A>
class ArraySliceC
{
public:
using Array_t = A;
using value_type = typename A::value_type;
using const_iterator = typename A::const_iterator;
ArraySliceC(const Array_t & source, size_t ifirst, size_t length)
: m_ifirst{ ifirst }
, m_length{ length }
, m_source{ source }
{
if (source.size() < (ifirst + length))
{
std::ostringstream os;
os << "ArraySliceC::ArraySliceC(<source>,"
<< ifirst << "," << length
<< "): out of bounds. (ifirst + length >= <source>.size())";
throw std::invalid_argument( os.str() );
}
}
size_t size() const
{
return m_length;
}
const value_type& at( size_t index ) const
{
return m_source.at( m_ifirst + index );
}
const value_type& operator[]( size_t index ) const
{
return m_source[m_ifirst + index];
}
const_iterator cbegin() const
{
return m_source.cbegin() + m_ifirst;
}
const_iterator cend() const
{
return m_source.cbegin() + m_ifirst + m_length;
}
private:
size_t m_ifirst;
size_t m_length;
const Array_t& m_source;
};
template <class T, size_t SZ>
std::ostream& operator<<( std::ostream& os, const std::array<T,SZ>& arr )
{
if (arr.size() == 0)
{
os << "[||]";
}
else
{
os << "[| " << arr.at( 0 );
for (auto it = arr.cbegin() + 1; it != arr.cend(); it++)
{
os << "," << (*it);
}
os << " |]";
}
return os;
}
template<class A>
std::ostream& operator<<( std::ostream& os, const ArraySliceC<A> & slice )
{
if (slice.size() == 0)
{
os << "^[||]";
}
else
{
os << "^[| " << slice.at( 0 );
for (auto it = slice.cbegin() + 1; it != slice.cend(); it++)
{
os << "," << (*it);
}
os << " |]";
}
return os;
}
template<class A>
A unfoldArray( std::function< typename A::value_type( size_t )> producer )
{
A result;
for (size_t i = 0; i < result.size(); i++)
{
result[i] = producer( i );
}
return result;
}
int main()
{
using A = std::array<float, 10>;
auto idf = []( size_t i ) -> float { return static_cast<float>(i); };
const auto values = unfoldArray<A>(idf);
std::cout << "values = " << values << std::endl;
// zero copy slice of values array.
auto sl0 = ArraySliceC( values, 2, 4 );
std::cout << "sl0 = " << sl0 << std::endl;
// zero copy slice of the sl0 (the slice of values array)
auto sl01 = ArraySliceC( sl0, 1, 2 );
std::cout << "sl01 = " << sl01 << std::endl;
return 0;
}