I have a set of sets where all the sizes are known at compile-time and will not be changed, and I want to pass this to a function.
Something akin to:
void someFunction(
std::tuple<std::array<float,SIZE1>,std::array<float,SIZE2>,std::array<float,SIZE3>> sets
) {
//...
}
With the number of inner std:arrays being variable and all the sizes of the std::arrays being variable.
Right now, I'm lost at where to start approaching this problem and would greatly appreciate any help.
Usually, you'd go with a variadic template for this:
template<std::size_t... sizes>
void someFunction(std::tuple<std::array<float,sizes>...> sets) {
// ...
}
The ... will expand std::array<float,sizes> for all sizes inside the tuple
A more generic solution, which also works with older standards is to accept any type:
template<class T>
void someFunction(const T& sets)
Since you appear to be passing arrays which may be large, using indirection is recommended.
You clarified that you prefer having separate parameters. The difference between using a single tuple, versus discrete parameters, is not really that much. The following solution requires at least parameter, and enforces all parameters to be std::arrays of the same size:
#include <tuple>
#include <array>
#include <type_traits>
#include <cstdlib>
template<size_t i,
typename ...T,
typename=std::enable_if_t<std::conjunction_v<
std::is_same<T, std::array<float, i>>...>>
>
void someFunction(const std::array<float, i> &first,
const T &...others)
{
}
void foo()
{
someFunction(std::array<float, 5>{}); // OK.
someFunction(std::array<float, 5>{},
std::array<float, 5>{}); // OK.
someFunction(std::array<float, 5>{},
std::array<float, 10>{}); // ERROR.
}
Some of the traits are C++17 and higher, but can be trivially re-implemented in C++11 and C++14.
Related
I want to apply certain functionality to some elements of a tuple based on a given condition or constraint of the types.
Below is a small dummy example where I want to call a callback function for all the elements in the tuple that holds an arithmetic type. In my current solution, I use std::apply and if constexpr on the type constraint as the condition to call the callback.
However, I assume this is iterating through all the elements of the tuple?
And now I am wondering if there is an alternative to do this. In my head, it should be possible to select the indices of the elements of interest at compile-time, because all the types are known and then apply the functionality only for the elements at those indices. But I don't know if this is possible, nor how to translate this to code.
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
template <typename... Args>
struct Foo {
using my_tuple = std::tuple<Args...>;
template <typename Fn>
void CallForArithmetic(Fn&& fn) {
auto arithmetic_call = [&]<typename T>(const T& x) {
if constexpr (std::is_arithmetic_v<T>) {
fn(x);
}
};
std::apply([&](auto&&... arg) { (arithmetic_call(arg), ...); }, tuple_);
};
private:
my_tuple tuple_;
};
int main() {
Foo<int, std::string> foo{};
int calls = 0;
// will be called only once for int type
foo.CallForArithmetic(
[&calls](const auto& arg) { std::cout << calls++ << '\n'; });
}
It's really "as if" you are iterating over the full tuple. In general, the compiled program will only do that if it has to, or if all optimisations are disabled, to aid debugging. And indeed, starting from -O1 (gcc) or -O2 (clang), a slightly simplified version of your code compiles to "return 1;":
main:
mov eax, 1
ret
Note: msvc finds the same result, but generates a lot of extra clutter.
So, as Jarod42 suggested, you can definitely expect your compiler to figure this out, and go with the solution that is easiest for you as a human. And if you're not sure, Compiler Explorer is a great tool for questions like yours.
I believe there are some ways to do this with MPL and fusion, but since I'm new to boost, it's kinda hard to figure out.
It doesn't matter whether the tuple is of std's or boost's, what I'm trying to ultimately achieve is:
template<T>
void some_func()
{
// declare a tuple contains types of which in boost::function_types::parameter_types<T>
// insert values to the tuple
// call std::apply()
}
I've tried using fold, but failed to figure out what should be in the third template argument.
Haven't tested it, but the following should work:
#include <type_traits>
#include <tuple>
#include <boost/mpl/at.hpp> // For at
namespace detail {
template<class ParamSequence, std::size_t ...Indices>
auto unpack_params(ParamSequence, std::index_sequence<Indices...>) -> std::tuple<boost::mpl::at<ParamSequence, Indices>...>;
}
template<class Function>
using param_tuple_t = decltype(detail::unpack_params(boost::function_types::parameter_types<Function>(), std::make_index_sequence<boost::function_types::function_arity<Function>::value>()>()));
Basically what I tried to do is
#include <array>
#include <initializer_list>
template <class... Fs>
auto F(const std::initializer_list<double>& vars, const Fs&... exprs) {
return std::array<double, vars.size()>{};
}
This of course does not compile since vars is not a compile-time constant in its length as known to compilers. The thing is, I can indeed know it's length at compile time, though the values are not known. And the problem is that if I expand the initializer list as variadic template, that would result in two variadic packs.
Do I have to pass explicit number parameter to the function? How do I achieve or approximate desired semantics?
EDIT: I don't have to return array, I just need to return a sequence of doubles, preferably on stack.
If you know the size at compile-time, why not use a fixed-size array with the length as template parameter?
#include <array>
template < size_t N, class... Fs >
auto F(const double (&vars)[N], const Fs&... exprs) {
return std::array<double, N>{};
}
int main()
{
auto a = F({ 1, 2, 3, 4 });
}
I have the following (not compilable) code:
template< size_t N >
void foo( std::array<int, N> )
{
// Code, where "N" is used.
}
int main()
{
foo( { 1,2 } );
}
Here, I want to pass an arbitrary number of ints to a function foo -- for convenience, I will use the std::initializer_list notation.
I tried to use an std::array to aggregate the ints (as shown in the code above), however, the compiler can not deduce the array size since the ints are passed as an std::initializer_list.
Using an std::initializer_list instead of an std::array also does not solve the problem since (in contrast to std::array) the size of the std::initializer_list is not captured as template argument.
Does anyone know which data structure can be used so that the ints can be passed by using the std::initializer_list notation and without passing the template argument N of foo explicitly?
Many thanks in advance
Thanks to core issue 1591, you can use
template <std::size_t N>
void foo( int const (&arr)[N] )
{
// Code, where "N" is used.
}
foo({1, 2, 3});
If using an initializer list is not a must, you can use variadic template parameter packs:
template<size_t S>
void foo_impl(array<int, S> const&)
{
cout << __PRETTY_FUNCTION__ << endl;
}
template<typename... Vals>
auto foo(Vals&&... vals) {
foo_impl<sizeof...(vals)>({ std::forward<Vals>(vals)... });
}
you'd call it as follows:
foo(1,2,3,4,5);
This defers common type checks until the initialization point of std::array (unless you add some admittedly ugly asserts), so you probably should prefer Columbo's answer.
I'm looking for a way to populate an array with elements of values at compile time rather then run time. So what I'm looking for is something like this
#define numbar
Bar foo[] = {Bar(0),Bar(1)....Bar(numbar)}
Does c++ have any way to do this? Perhaps using macro's or something like that?
Assuming c++14:
constexpr auto num_bars = 100;
class Bar {
constexpr Bar(int i);
};
template <std::size_t ...I>
constexpr auto generate_bars_impl(std::index_sequence<I...>) {
return {Bar(I)...};
}
template <std::size_t N, typename Indices = std::make_index_sequence<N>>
constexpr auto generate_bars() {
return generate_bars_impl(Indices());
}
constexpr auto generated = generate_bars<num_bars>();
This will give you an std::initializer_list in C++14. C++11: You should implement index_sequence and I think that initializer_list has no constexpr constructor.
I am not sure if this is clever enough, but in the case you could use boost preprocessor to create the code as described in you example:
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#define INIT(z, n, initializer)(initializer(n))
#define INIT_ARRAY(N, INITIALIZER) {BOOST_PP_SEQ_ENUM(BOOST_PP_REPEAT(5, INIT, Bar))}
struct Bar
{
constexpr Bar(int ii) : i(ii) {}
int i;
};
Bar data[] = INIT_ARRAY(5, Bar);
In general you can solve a lot of repetitive problems this way, but you often makes the code more complex and it less obvious what happens. So you should do this only if you have a lot of such think.
Of course you can also do some metaprogramming, which can lead to quite long compile times