I'm using variadic templates, and I would like to find the good way to unpack my parameters
template <typename kernel_type, typename ...kernel_types>
class MetaKernel : public MyKernel<kernel_type, kernel_types...> {
public:
MetaKernel (
unsigned int m,
unsigned int n,
const kernel_type& kernel_,
const kernel_types&... kernels_
) :
MyKernel<kernel_type, kernel_types...>(m, n)
{
Ks.set_max_size(sizeof...(kernel_types));
Ks.set_size(sizeof...(kernel_types));
// for each kernels_, add a MyObsKernel to Ks
// Ks[sizeof...(kernel_types)].reset((new MyObsKernel<kernel_type, kernel_types...>(kernels_, prototypes_, m, n))...);
}
private:
array < scoped_ptr < MyObsKernel<kernel_type, kernel_types...> > > Ks;
}
from the documentation (http://en.cppreference.com/w/cpp/language/parameter_pack), I saw how to unpack like this:
int dummy[sizeof...(Ts)] = { (std::cout << args, 0)... };
but as I'm working on scoped_ptr table, I need to initialize it with a "reset". so my solution does not work. How can I unpack my parameters using scoped_ptr ?
thanks for your help,
Jerome
You may initialize std::array with an initializer-list. Following may help: https://ideone.com/PTwatb
MetaKernel (unsigned int m, unsigned int n, const kernel_type& kernel_, const kernel_types&... kernels_) :
MyKernel<kernel_type, kernel_types...>(m, n),
Ks({scoped_ptr<MyObsKernel<kernel_type, kernel_types...> >(new MyObsKernel<kernel_type, kernel_types...>(kernels_, kernel_, m, n))...})
{
}
As I said in a comment, your question is incomplete. I understand that Ks is an std::array of size N = sizeof...(kernel_types), and that you want to call reset() on each element. If so, here's a solution:
struct _do { template<typename... A> _do(A&&...) { } };
template <typename kernel_type, typename ...kernel_types>
class MetaKernel : public MyKernel<kernel_type, kernel_types...>
{
using Base = MyKernel<kernel_type, kernel_types...>;
using Obs = MyObsKernel<kernel_type, kernel_types...>;
static constexpr size_t N = sizeof...(kernel_types);
public:
MetaKernel (
unsigned int m,
unsigned int n,
const kernel_type& kernel_,
const kernel_types&... kernels_
) : Base(m, n)
{
reset(std::make_integer_sequence<size_t, N>(), m, n, kernels_...);
}
private:
template<size_t... I>
void reset (
std::integer_sequence<size_t, I...>,
unsigned int m,
unsigned int n,
const kernel_types&... kernels_
)
{
_do{Ks[I].reset(new Obs(kernels_, /*prototypes_,*/ m, n))...};
}
array <scoped_ptr <Obs>, N> Ks;
};
I am making use of the auxiliary object _do and C++14 features std::integer_sequence, std::make_index_sequence. If this is not available, you can check Range here, where _do is also explained.
You need a second function apart from the constructor in order to deduce index pack I... and use it to align with parameters kernels_... so that each parameter is used in exactly one call of reset() on one element of Ks.
If you have more such unpacking operations, it is better to have I... available in the entire class and use it directly. You will then need just a second MetaKernelImpl class instead of adding a second version for each function. This class will have an additional template parameter that is specialized for std::integer_sequence<size_t, I...> for some deduced pack size_t... I. Again, have a look at Caller here to see how this can be done.
Related
To illustrate the situation, let's assume a minimal example: a Vector template class taking its dimension as a non-type template parameter. This class will provide x(), y() (etc.) accessors when the dimension allows it:
template <int N_dimension>
class Vector
{
public:
// ctors, etc.
int &x();
template <class = std::enable_if_t<(N_dimension>2)>> int &y();
private:
std::array<int, N_dimension> mData;
};
Yet, this does not work, because enable_if can only be applied on deduced template parameters.
Our current workaround looks cumbersome:
template <int N=N_dimension, class = std::enable_if_t<(N>2)>> int &y();
Moreover, it also requires a static-assert in the definition to make sure it is fool-proof (because now client code could give an explicit value to N that does not match the real dimension. Edit: Or an explicit value for the anonymous second template parameter, as pointed out by SergeyA).
Is there a more direct approach to express this in C++?
I would do away with SFINAE here, and simply split the code into interface and private implementation like that:
int& y() {
return y_impl(std::bool_constant<N > 2>{});
}
private:
int& y_impl(std::true_type ) {
// impl
}
int& y_impl(std::false_type ) {
static_assert(N > 2 /* always false */, "Wrong number of dimensions!");
}
The split here is in assumption of y not compilable when N <= 2, to reduce the clutter of error messages. If this is not the case, a single static_assert in y body would be sufficient.
In C++20, you might simply use requires to discard method:
template <int N>
class Vector
{
public:
int &x();
int &y() requires(N >= 2);
private:
std::array<int, N_dimension> mData;
};
In previous version, it is more verbose:
template <std::size_t N>
class Vector
{
public:
int &x();
template <std::size_t M = N, std::enable_if_t<(M >= 2 && M == N), int> = 0>
int &y();
private:
std::array<int, N_dimension> mData;
};
Suppose I have the following Matrix template class and there is a requirement to represent vector as either 1 x RowSize or ColSize x 1 matrix (so that I can reuse many matrix operators which are compatible with vectors: multiplying 2 matrices, multiplying matrix by a scalar etc):
template <class T, size_t ColumnSize, size_t RowSize>
struct Matrix {
T [ColumnSize][RowSize];
}
I have two questions:
1) If I am not mistaken I can achieve that either by partial specialization or using SFINAE on Matrix methods (for example to enable 'length' method when either ColSize or RowSize is 1). What are the pros and cons of mentioned options?
2) If I choose to go with the partial specialization, is there a way to define one specialization for both row and column vectors, instead of this:
template <class T, size_t ColumnSize>
struct Matrix<T, ColumnSize, 1> {
T length() const;
T [ColumnSize][RowSize];
}
template <class T, size_t RowSize>
struct Matrix<T, 1, RowSize> {
T length() const;
T [ColumnSize][RowSize];
}
It really depends on whether the requirement is "a general Matrix must not have a length method" (then SFINAE or inheritance should be used), or "length must not be called on a general Matrix" (then a static_assert inside of the length body is applicable). A third option is to not do anything and make length applicable on generic matrices, however there are still other operations that only work on vectors.
For "a general Matrix must not have a length method". To save space, I will use int, and shorter symbol names. Instead of int_, you should use std::integral_constant. The int_ wrapper is needed because of language restrictions that forbid specializing with more complex computations if the parameter is a non-type parameter. Therefore we ḿake the paramer a type, and wrap the value into it. The following does not use SFINAE, but inheritance. With d() of the vector mixing base class, you can access the data of the vector at any time from within the mixing class.
template<int> struct int_;
template<typename D, typename S>
struct V { };
template<typename T, int A, int B>
struct M : V<M<T, A, B>, int_<A * B>> {
T data[A][B];
};
template<typename T, int A, int B>
struct V<M<T, A, B>, int_<A + B - 1>> {
int length() const { return A * B; }
M<T, A, B> *d() { return static_cast<M<T, A, B>*>(this); }
const M<T, A, B> *d() const { return static_cast<const M<T, A, B>*>(this); }
};
This is now
int main() {
M<float, 1, 3> m1; m1.length();
M<float, 3, 1> m2; m2.length();
// M<float, 3, 2> m3; m3.length(); error
}
For "length must not be called on a general Matrix", you can use "static_assert"
template<typename T, int A, int B>
struct M {
int length() const {
static_assert(A == 1 || B == 1, "must not be called on a matrix!");
return A * B;
}
T data[A][B];
};
Choose what is most appropriate
SFINAE is only able to disable a template declaration based on its own parameters. It's a bit unnatural to disable a non-template member function such as length, using the parameters of the enclosing class. The technique looks like this:
template <class T, size_t RowSize, size_t ColumnSize>
struct Matrix {
// SFINAE turns a non-template into a template.
// Introduce a fake dependency so enable_if resolves upon function call.
template< typename size_t_ = size_t >
static constexpr
// Now write the actual condition within the return type.
std::enable_if_t< RowSize == 1 || ColumnSize == 1
, size_t_ > length() const;
{ return RowSize * ColumnSize; }
T [ColumnSize][RowSize];
}
If you can stomach this ugliness, then you get exactly what you want: a function of the desired type, which completely vanishes when the condition is not met. No other support is needed.
On the other hand, partial specialization affects the entire class definition. Since it's usually poor design to duplicate the entire class in each partial specialization, inheritance is used as Johannes describes.
Just to add one alternative to his answer, SFINAE can be used within partial specialization, to avoid the clever algebra and the int_ issue.
// Add "typename = void" for idiomatic class SFINAE.
template<size_t RowSize, size_t ColumnSize, typename = void>
struct maybe_vector_interface { }; // Trivial specialization for non-vectors
// Partial specialization for vectors:
template<size_t RowSize, size_t ColumnSize>
struct maybe_vector_interface< RowSize, ColumnSize,
std::enable_if_t< RowSize == 1 || ColumnSize == 1 > > {
static constexpr int length() const
{ return RowSize * ColumnSize; }
};
template<typename T, size_t RowSize, size_t ColumnSize>
struct Matrix
: maybe_vector_interface<RowSize, ColumnSize> {
T data[RowSize][ColumnSize];
};
Here is a code snippet illustrating my question:
const float D = 0.1F;
const float A[4] = {sin(0*D), sin(1*D), sin(2*D), sin(3*D)};
Imagine that global array A is much longer and you don't want to do all of this repetitive typing. Is there a shorter way to initialize array A at compile or initialization time, i.e. without having to write initialization function and call it somewhere in my program?
You could initialize A during dynamic initialization time as follows:
const float *init_a(float x_)
{
static float data[4];
for(unsigned i=0; i<4; ++i)
data[i]=sin(i*x_);
return data;
}
const float D=0.1f;
const float *A=init_a(D);
You may use code generator to generate initialization code. That is, write program that will write your initialization code for you. You may actually calculate values at generation-time.
Remember that C++ allows placing , after last element. It's also isn't necessary to specify array size. These two things should ease writing of generator.
This simple python code should work well:
from math import sin
print('const float A[', N, '] = {')
for i in range(N):
print('\t', sin(i*D), ',', sep='')
print('};')
Ok I just realized this doesn't actually answer the question, because it specifies "without having to write initialization function and call it somewhere in my program?" But I can't think of a convenient alternative.
template<size_t N>
std::array<float, N> GetLookupTable(float d)
{
std::array<float, N> table;
// .. populate table here
return table;
}
// a global somewhere
(static?) const auto Table_10x5 = GetLookupTable<10>(5.0f);
This first part is obsolete in C++14, but not long:
template<unsigned...>struct indexes { using type=indexes; };
template<unsigned Max, unsigned...Is>struct make_indexes:make_indexes<Max-1,Max-1,Is...>{};
template<unsigned...Is>struct make_indexes<0,Is...>:indexes<Is...>{};
template<unsigned Max>using make_indexes_t=typename make_indexes<Max>::type;
this is some template meta programming that lets us create and pass around bundles of indexes.
Then some code to generate the array:
namespace details {
template<std::size_t N, unsigned...Is>
std::array<float, N> poly_sin(float src, indexes<Is...>) {
return { (Is*sin(src))... };
}
}
template<std::size_t N>
std::array<float, N> poly_sin(float src) {
return details::poly_sin<N>( src, make_indexes_t<N>{} );
}
The first method takes indexes<Is...> and we plan that Is... is 0, 1, 2, ..., N-1. It then expands the parameter pack into a std::array of the right size.
make_indexes_t<N>{} expands (at compile time) to indexes<0, 1, 2, ..., N-1>{}, which is then passed to details::poly_sin, which then can deduce the Is... and use them within itself.
And point of use:
const float D = 0.1F;
const auto A = poly_sin<4>(D);
if you had a constexpr sin function, you could even make poly_sin a constexpr function and have it basically guaranteed to be evaluated at compile time.
If this is the case, make D constexpr and same with the two poly_sin functions.
As written, this occurs at dynamic initialization time.
While it appears that the array is copied twice, RVO elision means that any decent compiler will directly construct it in A.
If you want to be able to do this in general, first start with the above indexes code. Then add this:
template<class Sig>using result_of_t=typename std::result_of<Sig>::type;
namespace details {
template<std::size_t N, class F, unsigned... Is>
std::array< result_of_t< F(unsigned) >, N >
make_array( F&& f, indexes<Is...> ) {
return { f( Is )... };
}
}
template<std::size_t N, class F>
std::array< result_of_t< F(unsigned) >, N >
make_array( F&& f ) {
return details::make_array( std::forward<F>(f), make_indexes_t<N>{} );
}
const auto A = make_array<4>( [](unsigned i){ return float(i*sin(D)); } );
which uses a lambda to pass in the code that is repeated to build the array. Sadly, lambdas are not by default constexpr so you cannot do it at compile time.
You could either use boost.preprocessor and in particular the BOOST_PP_ENUM macro, like the example below:
#include <iostream>
#include <cmath>
#include <boost/preprocessor/repetition/enum.hpp>
#define SIZE 4
#define D 0.1
#define ORDER(z, n, text) std::sin(n * D)
double const A[SIZE] = { BOOST_PP_ENUM(SIZE, ORDER, ~) };
int main() {
for(auto i : A) std::cout << i << std::endl;
}
Or, you could use std::array instead of raw arrays, and via use of template meta-programming to generate a std::array at compile time. like the example below:
template<typename T, typename F, int SIZE, int... N>
constexpr std::array<T, SIZE>
genarray(F f) {
return std::array<T, SIZE>{{ f(N)... }};
}
template<typename T, typename F, int SIZE, int...> struct recursive_gen;
template<typename T, typename F, int SIZE, int... Args>
struct recursive_gen<T, F, SIZE, 0, Args...> {
static constexpr std::array<T, SIZE> generate(F f) {
return genarray<T, F, SIZE, 0, Args...>(f);
}
};
template<typename T, typename F, int SIZE, int N, int... Args>
struct recursive_gen<T, F, SIZE, N, Args...> {
static constexpr std::array<T, SIZE> generate(F f) {
return recursive_gen<T, F, SIZE, N - 1, N, Args...>::generate(f);
}
};
template<typename T, int SIZE>
struct array_generator {
template<typename F>
static constexpr std::array<T, SIZE> generate(F f) {
return recursive_gen<T, F, SIZE, SIZE - 1>::generate(f);
}
};
std::array<double, 4> const A = array_generator<double, 4>::generate([](int i) { return std::sin(0.1 * i);});
std::array<double, 4> const B = array_generator<double, 4>::generate([](int i) { return std::cos(0.1 * i);});
constexpr int fun(int i) { return 2 * i; }
constexpr std::array<int, 4> const C = array_generator<int, 4>::generate(fun); // generation during compile time
LIVE DEMO
Note however, that in order for generation to take place at compile time input function in array_generator must be constexpr. This is not the case for trigonometric functions (i.e., they are not constexpr). Thus initialization of arrays A and B will take place at initialization time, whereas generation of array C will take place at compile time.
Imagine that global array A is much longer and you don't want to do all of this repetitive typing. Is there a shorter way to initialize array A at compile or initialization time
Create a generator and pass it through std::generate_n() (or plain std::generate()).
#include <algorithm>
#include <array>
#include <cmath>
template <typename Value_t>
struct SinGenerator{
SinGenerator(std::size_t start = 0, Value_t counter_scalar = 1)
: index{start},
scalar{counter_scalar} {
}
Value_t operator()() {
return sin(index++ * scalar);
}
std::size_t index;
Value_t scalar;
};
template <typename Value_t, std::size_t Size>
std::array<Value_t, Size> init_table(const std::size_t start,
const Value_t counter_scalar) {
std::array<Value_t, Size> arr;
SinGenerator<Value_t> gen(start, counter_scalar);
std::generate(arr.begin(), arr.end(), gen);
return arr;
}
const auto kSinTable(init_table<float, 10>(0, 0.1f));
In case you A array will always stays the same and is very big, you can always write a short script, which calculates every value in array and output could be used in source code to have static initialization.
In case formula is simple, even MS Excel could be used to generate that kind of static initialization data.
I am writing code to perform Gaussian integration with n points, where n is a compile time constant.
For a given n, I know how to compute abscissas and weights. The computation has to be done from scratch for each different n.
Now, I do something along these lines:
// Several structs like this one (laguerre, chebyshev, etc).
template <size_t n>
struct legendre
{
static const size_t size = n;
static const double x[n];
static const double w[n];
};
template <typename Rule, typename F>
double gauss_quadrature (F&& f)
{
double acc = 0;
for (size_t j = 0; j < Rule::size; j++)
acc += Rule::w[j] * f (Rule::x[j]);
return acc;
}
to be used like this:
double i = gauss_quadrature<legendre<12>> (f);
Now, I can specialize in a translation unit the coefficients for legendre<12>, by doing
template <>
const legendre<12>::x[12] = { ... };
template <>
const legendre<12>::w[12] = { ... };
and everything is fine, as long as I only use 12-points Gauss-Legendre.
Now, I'm experimenting with different number of points, and I know how to generate the weights and nodes. I can for instance provide a routine
void compute_legendre_coeffs (size_t n, double* w, double* x);
and :
When I call gauss_quadrature<legendre<n>>, the template legendre<n> is automatically instantiated (this is the case).
When legendre<n> is instantiated for some compile-time n, I'd like the above compute_legendre_coeffs to be called at some point before main so that it fills the x and w member arrays. How do I achieve this ?
I know must define the arrays first:
template <size_t n>
const double legendre<n>::x[n] = {};
template <size_t n>
const double legendre<n>::w[n] = {};
but I can't come up with a method to initialize them. Anyone has a trick to do so ?
Convert the arrays to std::array:
#include <array>
template<int n> struct legendre {
static const std::array<double, n> x;
};
void compute_xs(int n, double *xs) {
...
}
template<int n> std::array<double, n> make_xs() {
std::array<double, n> xs;
compute_xs(n, xs.data());
return xs;
}
template<int n> const std::array<double, n> legendre<n>::x = make_xs<n>();
This does mean computing the x and w coefficients separately, though there are workarounds if this is less efficient, for example:
template<int n> struct legendre_coeffs {
std::array<double, n> x, w;
legendre_coeffs(): x(), w() { compute_legendre_coeffs(n, w.data(), x.data()); }
};
template<int n> struct legendre {
static const legendre_coeffs coeffs;
static const double (&x)[n], (&w)[n];
};
template<int n> const legendre_coeffs legendre<n>::coeffs;
template<int n> const double (&legendre<n>::x)[n]
= *reinterpret_cast<const double (*)[n]>(legendre<n>::coeffs::x.data());
template<int n> const double (&legendre<n>::w)[n]
= *reinterpret_cast<const double (*)[n]>(legendre<n>::coeffs::w.data());
template <size_t n>
class legendre
{
public:
static const size_t size = n;
static const double (&getX())[n] {
init();
return x;
}
static const double (&getW())[n] {
init();
return x;
}
private:
static double x[n];
static double w[n];
static void init() {
static bool _ = do_init(x,y);
}
static bool do_init( double *x, double *y ) {
// do the computation here, use local vars x, y
return true;
}
};
template <size_t n>
double legendre<n>::x[n];
template <size_t n>
double legendre<n>::w[n];
By providing an accessor you gain control of the entry point to your class. The accessors dispatch to a init function that uses initialization of a local static variable to call do_init only once in the program lifetime. The do_init does the actual initialization of the members.
Notes:
Depending on the compiler, this might not be thread safe (i.e. not all C++03 compilers provide thread safe initialization of static variables, which in turn means that the do_init might be called more than once in parallel, depending on the algorithm that might or not be an issue --i.e. if do_init computes the values aside and just writes them, the potential race condition is irrelevant as the net result will be the same). Some compilers offer mechanisms to guarantee one off execution (I believe boost has such a mechanism). Alternatively depending on your domain, you might be able to prime the coefficients before you start the threads.
The actual arrays cannot be const in this case, as the initialization needs to happen after they are created. That should not be an issue for anything other than possible micro optimizations (i.e. the compiler does not know about the values of the coefficients, so it cannot perform sub-expression evaluation at compile time).
First of all: you can't do initialization completely at compile-time using C++03 (yep, by design!) -- the only way to do it is to use C++ templates, but you'll unable to pass a double as template parameter.
Things getting better w/ C++11 -- you may use constexpr but only if your compute_legendre_coeffs() is trivial enough.
Or there is one trick I use when need to take some actions by fact of class declaration -- for example, register smth somewhere... to provide serialization capabilities via some library or smth like this.
You may use static constructors idiom to initalize that arrays... For simialr reasons I use the following code:
template <
typename Derived
, typename Target = Derived
>
class static_xtors
{
// This class will actually call your static init methods...
struct helper
{
helper()
{
Target::static_ctor();
}
~helper()
{
Target::static_dtor();
}
};
// ... because your derived class would inherit this member from static_xtor base
static helper s_helper;
// The rest is needed to force compiler to instantiate everything required stuff
// w/o eliminate as unused...
template <void(*)()>
struct helper2 {};
static void use_helper()
{
(void)s_helper;
}
helper2<&static_xtors::use_helper> s_helper2;
virtual void use_helper2()
{
(void)s_helper2;
}
public:
/// this is not required for your case... only if later you'll have
/// a hierarchy w/ virtuals
virtual ~static_xtors() {}
};
template <
typename Derived
, typename Target
>
typename static_xtors<Derived, Target>::helper
static_xtors<Derived, Target>::s_helper;
then you have to inherit static_xtors class, and implement two static methods: void static_ctor() -- which would initialize your arrays, and empty (in your case) void static_dtor()... I.e. smth like this:
template <size_t n>
struct legendre : public static_xtors<legendre<n>>
{
static const size_t size = n;
static double x[n];
static double w[n];
static void static_ctor()
{
compute_legendre_coeffs(n, x, w);
}
static void static_dtor()
{
// nothing to do
}
};
template <size_t n>
static double legendre<n>::x[n];
template <size_t n>
static double legendre<n>::w[n];
As you may notice, x and w is not const anymore :( -- you may try to make them const again hiding to private and add static getters to be used by callers... Also, your internal arrays would be initialized at runtime, but before the main function (and just once)...
or play w/ constexpr... but seems you'll need to redesign you initializer function (somehow) because using initialization lists it should looks like this:
template <size_t n>
static double legendre<n>::x[n] = { calc_coeff_x<0>(), calc_coeff_x<1>(), calc_coeff_x<2>(), ... }
... and definitely you can't do it w/o specializing (and extensive macros usage).
But probably variadic templates may help... need to know more details about your function and time to think :))
Maybe you can try turning your function into an initializer class template, whose parameter would be the class/struct you want to initialize. Then change that class template to include a constant instance of the initializer . Finally, have the constructor of the initializer class trigger the code doing the actual initialization.
You should perhaps also protect access [1] to the initializer class so that the initialization doesn't happen more than once.
The idea, as you can see, is to use the facts that class instances get their constructor code called, template instances get their constant data initialized.
Below is a possible (and simple) implementation, without template:
struct legendre_init {
legendre_init(){
compute_legendre_coeffs (T::n, T::w, T::x);
}
};
template <size_t n>
struct legendre
{
typedef legendre<n> self_type;
static const size_t size = n;
static const double x[n];
static const double w[n];
static const legendre_init _l;
};
Here's another take on it, this time putting the initialization in the struct directly:
template <class T>
class T_init {
public:
T_init(){
T::_init();
}
};
template <size_t n>
struct legendre
{
typedef legendre<n> self_type;
static const size_t size = n;
static const double x[n];
static const double w[n];
static const T_init<self_type> _f;
static void _init(){
compute_legendre_coeffs (self_type::n, self_type::w, self_type::x);
}
};
The interesting characteristic of this solution is that the T_init constant instance shouldn't take any space within the T struct. The initialization logic is bundled with the class that needs it, and the T_init template only enables it automatically.
[1] Xeo mentioned the std::call_once template, which could come handy here.
I have a class with operator() like this:
struct S
{
int operator()(int a, int b, int c, int d);
};
Example usage:
S s;
int i = s(1, 2, 3, 4);
I need my users to be able to use an alternate syntax:
int i = s[1][2][3][4]; // equivalent to calling s(1, 2, 3, 4)
I know I need to add S::operator[](int a) and that it needs to return a helper object. But beyond that it all gets a bit complex and I have a feeling that I am reinventing the wheel since other libraries (e.g. multidimensional arrays) probably already offer similar interface.
Ideally I'd just use an existing library to achieve this goal. Failing that, how can I achieve my goal with the most generic code?
Edit: ideally I'd like to achieve this without any runtime penalty on a modern optimizing compiler.
Here we go!
First of all, the code is kind of messy- I have to accumulate the argument values as we go, and the only way I could think of (at least in C++03) is to pass the immediate indices set around as arrays.
I have checked this on G++ 4.5.1 (Windows / MinGW) and I confirm that on -O3 the call:
s[1][2][3][4];
yields the same assembler code as:
s(1,2,3,4);
So - no runtime overhead if your compiler is smart with optimisations. Good job, GCC team!
Here goes the code:
#include <iostream>
template<typename T, unsigned N, unsigned Count>
struct PartialResult
{
static const int IndicesRemembered = Count-1-N;
T& t;
int args[IndicesRemembered];
PartialResult(T& t, int arg, const int* rest) : t(t) {
for (int i=0; i<IndicesRemembered-1; ++i) {
args[i] = rest[i];
}
if (IndicesRemembered>0) args[IndicesRemembered-1] = arg;
}
PartialResult<T, N-1, Count> operator[](int k) {
return PartialResult<T, N-1, Count>(t, k, args);
}
};
template<typename T, unsigned Count>
struct PartialResult<T, 0, Count>
{
static const int IndicesRemembered = Count-1;
T& t;
int args[IndicesRemembered];
PartialResult(T& t, int arg, const int* rest) : t(t) {
for (int i=0; i<IndicesRemembered-1; ++i) {
args[i] = rest[i];
}
if (IndicesRemembered>0) args[IndicesRemembered-1] = arg;
}
void operator[](int k) {
int args2[Count];
for (int i=0; i<Count-1; ++i) {
args2[i] = args[i];
}
args2[Count-1] = k;
t(args2);
}
};
template<typename T, unsigned Count>
struct InitialPartialResult : public PartialResult<T, Count-2, Count> {
InitialPartialResult(T& t, int arg)
: PartialResult<T, Count-2, Count>(t, arg, 0) {}
};
struct C {
void operator()(const int (&args)[4]) {
return operator()(args[0], args[1], args[2], args[3]);
}
void operator()(int a, int b, int c, int d) {
std::cout << a << " " << b << " " << c << " " << d << std::endl;
}
InitialPartialResult<C, 4> operator[](int m) {
return InitialPartialResult<C, 4>(*this, m);
}
};
And seriously, please, don't use this and just stick with operator(). :) Cheers!
This is an attempt at the bind approach. I doubt that it's particularly efficient, and it has some nasty bits in it, but I post it in case anyone knows how to fix it. Please edit:
template <int N>
struct Helper {
function_type<N>::type f;
explicit Helper(function_type<N>::type f) : f(f) {}
Helper<N-1> operator[](int p) {
return Helper<N-1>(bound<N-1>(f,p));
}
};
template<>
struct Helper<0> {
function_type<0>::type f;
explicit Helper(function_type<0>::type f) : f(f) {}
operator int() {
return f();
}
};
Helper<3> S::operator[](int p) {
return Helper<3>(std::bind(s, _1, _2, _3));
}
where s is an expression that returns operator() bound to this. Something along the lines of std::bind(std::mem_fun(S::operator(), this, _1, _2, _3, _4)). Although I can't remember whether std::bind can already handle member functions, mem_fun might not be needed.
function_type<N>::type is std::function<int, [int, ... n times]>, and bound<N> is function_type<N>::type bound(function_type<N+1>::type f, int p) { return std::bind(f, p, _1, _2, ... _N); }. I'm not immediately sure how to define those recursively, but you could just list them up to some limit.
I would avoid this altogether and offer just operator(), but if you really want to give it a shot, the idea is that your type's operator[] would return an object of a helper type that holds both a reference to your object and the value that was passed in. That helper class will implement operator[] by again storing a reference to the original object and the arguments to both calls to []. This would have to be done for all but the last level (I.e. a fair amount of helpers). I the last level, operator[] will take its argument together with all previously stored values and call operator() with all of the previously stored values plus the current value.
A common way of phrasing this is saying that each intermetiate type binds one of the arguments of the call to operator(), with the last one executing the call with all bound arguments.
Depending on whether you want to support more or less number of dimensions of arrays you might want/need to complicate this even more to make it generic. In general it is not worth the effort and just offering operator() is usually the solution. Remember that it is better to keep things as simple as possible: less effort to write and much less effort to maintain.
Here is a Fusion implementation that supports arbitrary parameter and return types. Kudos to anyone that can get this working (please let me know if you do)!
template <class Derived, class ReturnValue, class Sequence>
struct Bracketeer
{
typedef ReturnValue result_type;
typedef boost::fusion::result_of::size<Sequence> Size;
struct RvBase
{
Sequence sequence;
Derived *derived;
};
template <int n>
struct Rv : RvBase
{
Rv(Derived *d) { this->derived = d; }
Rv(RvBase *p) : RvBase(*p) { }
Rv<n-1> operator[](typename boost::fusion::result_of::at_c<Sequence const, n-1>::type v)
{
boost::fusion::at_c<Size::value - 1 - n>(sequence) = v;
return Rv<n-1>(this);
}
};
template <>
struct Rv<0> : RvBase
{
Rv(Derived *d) { this->derived = d; }
Rv(RvBase *p) : RvBase(*p) { }
ReturnValue operator[](typename boost::fusion::result_of::at_c<Sequence, Size::value - 1>::type v)
{
boost::fusion::at_c<Size::value - 1>(sequence) = v;
return invoke(*derived, sequence);
}
};
Rv<Size::value - 1> operator[](typename boost::fusion::result_of::at_c<Sequence, 0>::type v)
{
Rv<Size::value> rv(static_cast<Derived*>(this));
return rv[v];
}
};
struct S
:
Bracketeer<S, int, boost::fusion::vector<int, int, int, int> >
{
int operator()(int a, int b, int c, int d);
};