array length deduction from argument - c++

Given a function with the parameter signature std::array<const size_t, V> shape like:
template<int V>
struct Cls {int val(){return V;} };
template <int V>
auto make(std::array<const size_t, V> shape) -> Cls<V>{
return Cls<V>();
}
I need to always add the template parameter V like
auto t1 = make<2>({2, 3}); // does work but need the information twice
std::cout << t1.val() << std::endl;
as the braced-initializer list seems to be casted into a c++11 std::array. However, this seems to me redundant. The length of {2, 3} is two. I know it and the compiler should also know it. Is it possible to wrap it into:
// auto t0 = make({2, 3}); // doesn't work
// auto t0 = make(2, 3); // would be also ok
I tried something like determining the size of the std::array as a constexpr. But I can't get rid of the template argument <2>.
template <int V>
constexpr size_t arr_len(std::array<const size_t, V>){return V;}
template <typename V>
auto make2(V shape) -> Cls<arr_len(shape)>{
return Cls<arr_len(shape)>();
}
I add a runnable MWE here:
https://ideone.com/wrVe9M
This thread seems to be related but I do not see how it might help.
Any suggestions?

I suppose you can use a variadic make() function (make0() in the following example) that can calculate the size as sizeof...(Is)
The following is a full working example
#include <array>
#include <iostream>
template<int V>
struct Cls {int val(){return V;} };
template <int V>
auto make(std::array<const size_t, V> shape) -> Cls<V>{
return Cls<V>();
}
template <typename ... Is>
auto make0 (Is ... is) -> Cls<sizeof...(Is)>
{ return make<sizeof...(Is)>({ {std::size_t(is)... } }); }
int main ()
{
auto t1 = make0(2, 3, 5, 7);
std::cout << t1.val() << std::endl;
}

Related

How to return multi- dimensional-vector of given dimension and type?

Is it possible in modern c++ to write a function that, given a number n and a type, return a n-dimensional vector type.
1,float -> return std::vector<float>
2,float -> return std::vector<std::vector<float>>
and so on...
Here is a compile time approach using template specialization. We apply std::vector<> recursively until we reach the specialization for N == 0, where the final type is set to T:
#include <type_traits>
#include <vector>
template<std::size_t N, typename T>
struct recursive_vector {
using type = std::vector<typename recursive_vector<N - 1, T>::type>;
};
template<typename T>
struct recursive_vector<0, T> {
using type = T;
};
template<std::size_t N, typename T>
using vec = recursive_vector<N, T>::type;
so vec<3, float> becomes vector<vector<vector<float>>>:
int main() {
vec<3, float> v;
using std::vector;
static_assert(std::is_same_v<decltype(v), vector<vector<vector<float>>>>);
}
Try out the code, here.
Is it possible in modern C++ to write a function that given a number n and a type return a n-dimensional vector type?
Compile time
If n is known at compile time, the job easy, which is already given in the other answer. Alternatively, you could also do:
#include <vector>
template<typename T, std::size_t N>
struct vec : public vec<std::vector<T>, N - 1u> {};
template<typename T> struct vec<T, 0u> { using type = T; };
template<typename T, std::size_t N> using vec_t = typename vec<T, N>::type;
Now vec_t<float, 5u> means std::vector<std::vector<std::vector<std::vector<std::vector<float>>>>>
(See a live demo in godbolt.org)
Run time
When n is run time depended, one possible solution is to wrap the possible multidimensional vectors types to std::variant (Since c++17).
Following is an example code.
#include <vector>
#include <variant> // std::variant
// type aliases for the multi-dim vectors
template<typename T> using Vec1D = std::vector<T>;
template<typename T> using Vec2D = Vec1D<Vec1D<T>>;
template<typename T> using Vec3D = Vec1D<Vec2D<T>>;
// ... so on (hope that you wouldn't need more than 4 or 5!)
template<typename T>
using VecVariants = std::variant<Vec1D<T>, Vec2D<T>, Vec3D<T>/*, so on ...*/>;
template<typename T> auto getVector_of(const std::size_t n)
{
if (n == 1) return VecVariants<T>{Vec1D<T>{}};
if (n == 2) return VecVariants<T>{Vec2D<T>{}};
if (n == 3) return VecVariants<T>{Vec3D<T>{}};
// ... so on
return VecVariants<T>{Vec3D<T>{}}; // some default return!
}
template<typename T> void doSomething(const VecVariants<T>& vec)
{
std::visit([](auto&& arg) {
using Type = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<Type, Vec1D<T>>)
std::cout << "... do something with Vec1D\n";
else if constexpr (std::is_same_v<Type, Vec2D<T>>)
std::cout << "... do something with Vec2D\n";
else if constexpr (std::is_same_v<Type, Vec3D<T>>)
std::cout << "... do something with Vec3D\n";
// .... so on
else
std::cout << "... default!\n";
}, vec);
}
Now you can pass run time n
int main()
{
std::size_t n{ 5 };
while (n)
doSomething(getVector_of<float>(n--)); // n must be 0u < n <= MAX_n you defined!
}
(See a live demo in godbolt.org)

can I deduce a tuple element type in a function parameter (ie with std::tuple_element)?

I have a struct which stores some data in a tuple. I want to build a function getWithDefault<n>(m), which gets the data from the nth term but replaces it with m if the value is 0. But to do this, I have to know the correct data type for m in the function parameter: getWithDefault<>(WhichType?). Is there any way of doing this? I've tried with std::tuple_element, but it doesn't seem to work.
#include <iostream>
#include <tuple>
template <typename... T>
struct C
{
C(T... args) : t(std::make_tuple(args...)) {}
template <int n>
auto get() const
{ return std::get<n>(t); }
template <int n>
auto getWithDefault(std::tuple_element<n, decltype(t)>::type de) // Compiler error: identifier not found
{
const auto v = get<n>();
return v != 0 ? v : de;
}
private:
const std::tuple<T...> t;
};
int main()
{
C<int, int> c(0, 4);
std::cout << c.getWithDefault<0>(5); // this should return 5
return 0;
}
I can see why this code fails--std::tuple_element doesn't have access to the member variable from inside the function parameter. So, is there a viable way of deducing the type of a tuple term from within a function parameter?
There is no need to deduce the type, because you already know it. Maybe you refer to using decltype, but you also do not need that (you can, but need not). Anyhow...
For convenience you can use an alias template:
template <size_t n>
using nth_type = typename std::tuple_element<n,std::tuple<T...>>::type;
Full example:
#include <iostream>
#include <tuple>
template <typename... T>
struct C
{
C(T... args) : t(std::make_tuple(args...)) {}
template <size_t n>
using nth_type = typename std::tuple_element<n,std::tuple<T...>>::type;
template <int n>
auto get() const
{ return std::get<n>(t); }
template <size_t n>
auto getWithDefault(nth_type<n> de)
{
const auto v = get<n>();
return v != 0 ? v : de;
}
private:
const std::tuple<T...> t;
};
int main()
{
C<int, int> c(0, 4);
std::cout << c.getWithDefault<0>(5); // this should return 5
return 0;
}
You just have order of declaration issue (and a missing typename), move t before getWithDefault:
template <typename... T>
struct C
{
private:
const std::tuple<T...> t;
public:
C(T... args) : t(std::make_tuple(args...)) {}
template <int n>
auto get() const
{ return std::get<n>(t); }
template <int n>
auto getWithDefault(std::tuple_element_t<n, decltype(t)> de)
{
const auto v = get<n>();
return v != 0 ? v : de;
}
};
Demo

Specializing function template based on lambda arity

I am trying to specialize a templated function based on the arity of the lambda that I pass to it as an argument. This is what I have come up with for a solution:
template<typename Function, bool>
struct helper;
template<typename Function>
struct helper<Function, false>
{
auto operator()(Function&& func)
{
std::cout << "Called 2 argument version.\n";
return func(1, 2);
}
};
template<typename Function>
struct helper<Function, true>
{
auto operator()(Function&& func)
{
std::cout << "Called 3 argument version.\n";
return func(1, 2, 3);
}
};
template<typename T>
struct B
{
T a;
const T someVal() const { return a; }
};
template<typename Function, typename T>
auto higherOrderFun(Function&& func, const T& a)
{
return helper<Function, std::is_invocable<Function, decltype(a.someVal()), decltype(a.someVal()), decltype(a.someVal())>::value>{}(std::forward<Function>(func));
}
int main()
{
B<int> b;
std::cout << higherOrderFun([](auto x, auto y) {return x+y; }, b) << "\n";
std::cout << higherOrderFun([](auto x, auto y, auto z) {return x + y+z; }, b) << "\n";
return 0;
}
Is there a way to achieve this in a more elegant manner? I've looked through this: Arity of a generic lambda
However, the latest solution (florestan's) turns all arguments into aribtrary_t, so one has to cast them back inside of each lambda, which I do not find ideal. Ideally I would have liked to directly specialize the templated higherOrderFun with SFINAE, but as it is I use a helper class in order to achieve that. Is there a more straighforward way? For instance to apply SFINAE directly to higherOrderFun without relying on a helper class? The whole point of this is to not have to change higherOrderFun into higherOrderFun2 and higherOrderFun3, but rather have the compiler deduce the correct specialization from the lambda and the given argument (const T& a).
I should mention that I also don't care about the type of the arguments to the function - just about their count, so I would have changed decltype(a.someVal()) to auto in my example if that was possible (maybe there's a way to circumvent explicitly defining the types?).
The following template gives me the number of parameters to a lambda, a std::function, or a plain function pointer. This seems to cover all the basics. So, you specialize on n_lambda_parameters<T>::n, and plug this into your template. Depending on your specific use cases, you may need to employ the facilities offered by std::remove_reference_t or std::decay_t, to wrap this.
Tested with g++ 9. Requires std::void_t from C++17, plenty of examples of simulating std::void_t pre C++17 can be found elsewhere...
#include <functional>
// Plain function pointer.
template<typename T> struct n_func_parameters;
template<typename T, typename ...Args>
struct n_func_parameters<T(Args...)> {
static constexpr size_t n=sizeof...(Args);
};
// Helper wrapper to tease out lambda operator()'s type.
// Tease out closure's operator()...
template<typename T, typename> struct n_extract_callable_parameters;
// ... Non-mutable closure
template<typename T, typename ret, typename ...Args>
struct n_extract_callable_parameters<T, ret (T::*)(Args...) const> {
static constexpr size_t n=sizeof...(Args);
};
// ... Mutable closure
template<typename T, typename ret, typename ...Args>
struct n_extract_callable_parameters<T, ret (T::*)(Args...)> {
static constexpr size_t n=sizeof...(Args);
};
// Handle closures, SFINAE fallback to plain function pointers.
template<typename T, typename=void> struct n_lambda_parameters
: n_func_parameters<T> {};
template<typename T>
struct n_lambda_parameters<T, std::void_t<decltype(&T::operator())>>
: n_extract_callable_parameters<T, decltype(&T::operator())> {};
#include <iostream>
void foo(int, char, double=0)
{
}
int main()
{
auto closure=
[](int x, int y)
// With or without mutable, here.
{
};
std::cout << n_lambda_parameters<decltype(closure)>::n
<< std::endl; // Prints 2.
std::cout << n_lambda_parameters<decltype(foo)>::n
<< std::endl; // Prints 3.
std::cout << n_lambda_parameters<std::function<void (int)>>::n
<< std::endl; // Prints 1.
return 0;
}
I would use different overloads:
template<typename Function>
auto higherOrderFun(Function&& func)
-> decltype(std::forward<Function>(func)(1, 2, 3))
{
return std::forward<Function>(func)(1, 2, 3);
}
template<typename Function>
auto higherOrderFun(Function&& func)
-> decltype(std::forward<Function>(func)(1, 2))
{
return std::forward<Function>(func)(1, 2);
}
Possibly with overload priority as
struct low_priority {};
struct high_priority : low_priority{};
template<typename Function>
auto higherOrderFunImpl(Function&& func, low_priority)
-> decltype(std::forward<Function>(func)(1, 2))
{
return std::forward<Function>(func)(1, 2);
}
template<typename Function>
auto higherOrderFunImpl(Function&& func, high_priority)
-> decltype(std::forward<Function>(func)(1, 2))
{
return std::forward<Function>(func)(1, 2);
}
template<typename Function>
auto higherOrderFun(Function&& func)
-> decltype(higherOrderFun(std::forward<Function>(func), high_priority{}))
{
return higherOrderFun(std::forward<Function>(func), high_priority{});
}
If you want to use the arity traits from florestan, it might result in:
template<typename F>
decltype(auto) higherOrderFun(F&& func)
{
if constexpr (arity_v<std::decay_t<F>, MaxArity> == 3)
{
return std::forward<F>(func)(1, 2, 3);
}
else if constexpr (arity_v<std::decay_t<F>, MaxArity> == 2)
{
return std::forward<F>(func)(1, 2);
}
// ...
}

Associating an array with a variadic template

I'm now learning a little about templates and templates in C++11, C++14 and C++1z. I'm trying to write a variadic class template with an inside class that will associate an int to every template argument - and have a constexpr method that returns its array representation.
Let's say that I have ensured that the template cannot receive two of the same type as an argument. I was thinking about doing it somewhat like this:
template <typename... Types>
struct MyVariadicTemplate {
//we know that all types in Types... are different
template <int... Values>
struct MyInnerTemplate {
//I need to make sure that sizeof...(Values) == sizeof...(Types)
constexpr std::array<int, sizeof...(Values)> to_array() {
std::array<int, sizeof...(Values)> result = {Values...};
return result;
// this is only valid since C++14, as far as I know
}
};
};
this code should be valid (if it's not, I'd love to know why). Now, I'd like to add another inner template:
template <typedef Type>
struct AnotherInnerTemplate {};
that has a public typedef, which represents MyInnerTemplate with one on the position of Type in Types... and zeros elsewhere - and here I'm lost. I don't know how to proceed
I would appreciate any hint on how that can be done - and if I'm heading towards the wrong direction, I hope somebody can give me a hint on how to do that.
I think what you're looking for is something like this.
#include <array>
#include <cstddef>
#include <iostream>
#include <type_traits>
template <typename NeedleT, typename... HaystackTs>
constexpr auto get_type_index_mask() noexcept
{
constexpr auto N = sizeof...(HaystackTs);
return std::array<bool, N> {
(std::is_same<NeedleT, HaystackTs>::value)...
};
}
template <typename T, std::size_t N>
constexpr std::size_t ffs(const std::array<T, N>& array) noexcept
{
for (auto i = std::size_t {}; i < N; ++i)
{
if (array[i])
return i;
}
return N;
}
int
main()
{
const auto mask = get_type_index_mask<float, bool, int, float, double, char>();
for (const auto& bit : mask)
std::cout << bit;
std::cout << "\n";
std::cout << "float has index " << ffs(mask) << "\n";
}
Output:
00100
float has index 2
The magic happens in the parameter pack expansion
(std::is_same<NeedleT, HaystackTs>::value)...
where you test each type in HaystackTs against NeedleT. You might want to apply std::decay to either type if you want to consider, say, const int and int the same type.
template <int size, int... Values> struct AnotherImpl {
using Type = typename AnotherImpl<size - 1, Values..., 0>::Type;
};
template <int... Values> struct AnotherImpl<0, Values...> {
using Type = Inner<Values...>;
};
template <class T> struct Another {
using Type = typename AnotherImpl<sizeof...(Types) - 1, 1>::Type;
};
Full:
template <class... Types> struct My {
template <int... Values> struct Inner {
constexpr std::array<int, sizeof...(Values)> to_array() {
return std::array<int, sizeof...(Values)>{Values...};
}
};
template <int size, int... Values> struct AnotherImpl {
using Type = typename AnotherImpl<size - 1, Values..., 0>::Type;
};
template <int... Values> struct AnotherImpl<0, Values...> {
using Type = Inner<Values...>;
};
template <class T> struct Another {
using Type = typename AnotherImpl<sizeof...(Types) - 1, 1>::Type;
};
};
auto main() -> int {
My<int, float, char>::Another<int>::Type s;
auto a = s.to_array();
for (auto e : a) {
cout << e << " ";
}
cout << endl;
return 0;
}
prints:
1 0 0
Is this what you want?

Implementing std::array-like constructors in other classes

In all the modern C++ compilers I've worked with, the following is legal:
std::array<float, 4> a = {1, 2, 3, 4};
I'm trying to make my own class that has similar construction semantics, but I'm running into an annoying problem. Consider the following attempt:
#include <array>
#include <cstddef>
template<std::size_t n>
class float_vec
{
private:
std::array<float, n> underlying_array;
public:
template<typename... Types>
float_vec(Types... args)
: underlying_array{{args...}}
{
}
};
int main()
{
float_vec<4> v = {1, 2, 3, 4}; // error here
}
When using int literals like above, the compiler complains it can't implicitly convert int to float. I think it works in the std::array example, though, because the values given are compile-time constants known to be within the domain of float. Here, on the other hand, the variadic template uses int for the parameter types and the conversion happens within the constructor's initializer list where the values aren't known at compile-time.
I don't want to do an explicit cast in the constructor since that would then allow for all numeric values even if they can't be represented by float.
The only way I can think of to get what I want is to somehow have a variable number of parameters, but of a specific type (in this case, I'd want float). I'm aware of std::initializer_list, but I'd like to be able to enforce the number of parameters at compile time as well.
Any ideas? Is what I want even possible with C++11? Anything new proposed for C++14 that will solve this?
A little trick is to use constructor inheritance. Just make your class derive from another class which has a pack of the parameters you want.
template <class T, std::size_t N, class Seq = repeat_types<N, T>>
struct _array_impl;
template <class T, std::size_t N, class... Seq>
struct _array_impl<T, N, type_sequence<Seq...>>
{
_array_impl(Seq... elements) : _data{elements...} {}
const T& operator[](std::size_t i) const { return _data[i]; }
T _data[N];
};
template <class T, std::size_t N>
struct array : _array_impl<T, N>
{
using _array_impl<T, N>::_array_impl;
};
int main() {
array<float, 4> a {1, 2, 3, 4};
for (int i = 0; i < 4; i++)
std::cout << a[i] << std::endl;
return 0;
}
Here is a sample implementation of the repeat_types utility. This sample uses logarithmic template recursion, which is a little less intuitive to implement than with linear recursion.
template <class... T>
struct type_sequence
{
static constexpr inline std::size_t size() noexcept { return sizeof...(T); }
};
template <class, class>
struct _concatenate_sequences_impl;
template <class... T, class... U>
struct _concatenate_sequences_impl<type_sequence<T...>, type_sequence<U...>>
{ using type = type_sequence<T..., U...>; };
template <class T, class U>
using concatenate_sequences = typename _concatenate_sequences_impl<T, U>::type;
template <std::size_t N, class T>
struct _repeat_sequence_impl
{ using type = concatenate_sequences<
typename _repeat_sequence_impl<N/2, T>::type,
typename _repeat_sequence_impl<N - N/2, T>::type>; };
template <class T>
struct _repeat_sequence_impl<1, T>
{ using type = T; };
template <class... T>
struct _repeat_sequence_impl<0, type_sequence<T...>>
{ using type = type_sequence<>; };
template <std::size_t N, class... T>
using repeat_types = typename _repeat_sequence_impl<N, type_sequence<T...>>::type;
First of what you are seeing is the default aggregate initialization. It has been around since the earliest K&R C. If your type is an aggregate, it supports aggregate initialization already. Also, your example will most likely compile, but the correct way to initialize it is std::array<int, 3> x ={{1, 2, 3}}; (note the double braces).
What has been added in C++11 is the initializer_list construct which requires a bit of compiler magic to be implemented.
So, what you can do now is add constructors and assignment operators that accept a value of std::initializer_list and this will offer the same syntax for your type.
Example:
#include <initializer_list>
struct X {
X(std::initializer_list<int>) {
// do stuff
}
};
int main()
{
X x = {1, 2, 3};
return 0;
}
Why does your current approach not work? Because in C++11 std::initializer_list::size is not a constexpr or part of the initializer_list type. You cannot use it as a template parameter.
A few possible hacks: make your type an aggregate.
#include <array>
template<std::size_t N>
struct X {
std::array<int, N> x;
};
int main()
{
X<3> x = {{{1, 2, 3}}}; // triple braces required
return 0;
}
Provide a make_* function to deduce the number of arguments:
#include <array>
template<std::size_t N>
struct X {
std::array<int, N> x;
};
template<typename... T>
auto make_X(T... args) -> X<sizeof...(T)>
// might want to find the common_type of the argument pack as well
{ return X<sizeof...(T)>{{{args...}}}; }
int main()
{
auto x = make_X(1, 2, 3);
return 0;
}
If you use several braces to initialize the instance, you can leverage list-init of another type to accept these conversions for compile-time constants. Here's a version that uses a raw array, so you only need parens + braces for construction:
#include <array>
#include <cstddef>
template<int... Is> struct seq {};
template<int N, int... Is> struct gen_seq : gen_seq<N-1, N-1, Is...> {};
template<int... Is> struct gen_seq<0, Is...> : seq<Is...> {};
template<std::size_t n>
class float_vec
{
private:
std::array<float, n> underlying_array;
template<int... Is>
constexpr float_vec(float const(&arr)[n], seq<Is...>)
: underlying_array{{arr[Is]...}}
{}
public:
constexpr float_vec(float const(&arr)[n])
: float_vec(arr, gen_seq<n>{})
{}
};
int main()
{
float_vec<4> v0 ({1, 2, 3, 4}); // fine
float_vec<4> v1 {{1, 2, 3, 4}}; // fine
float_vec<4> v2 = {{1, 2, 3, 4}}; // fine
}
Explicitly specify that the data type for the initialization to floating point type. You can do this by doing "1.0f" instead of putting "1". If it is a double, put "1.0d". If it is a long, put "1l" and for unsigned long put "1ul" and so on..
I've tested it here: http://coliru.stacked-crooked.com/a/396f5d418cbd3f14
and here: http://ideone.com/ZLiMhg
Your code was fine. You just initialized the class a bit incorrect.
#include <array>
#include <cstddef>
#include <iostream>
template<std::size_t n>
class float_vec
{
private:
std::array<float, n> underlying_array;
public:
template<typename... Types>
float_vec(Types... args)
: underlying_array{{args...}}
{
}
float get(int index) {return underlying_array[index];}
};
int main()
{
float_vec<4> v = {1.5f, 2.0f, 3.0f, 4.5f}; //works fine now..
for (int i = 0; i < 4; ++i)
std::cout<<v.get(i)<<" ";
}