0

I'm trying to make a simple way to generate std::array's at compile time. It's been a struggle so far to figure out a good way to pass a constexpr function in at compile time. The workaround I have found so far is like this.

#include <iostream>                                                                
#include <array>                                                                   
namespace a {                                                                      
constexpr int f(const int & i) { return i * i * i;}                                
#include "generate_with_function.hpp"                                              
}                                                                                  
int main()                                                                         
{                                                                                  
    auto arr  = a::generator<false,10,0>::array;                                   
    for (auto i : arr) {                                                           
        std::cout << i << " ";                                                     
    }                                                                              
    return 0;                                                                      
}  

this basically assumes you will define a function called f and I wrapped it in a namespace incase I wanted to do a different one. I wanted to know if there is a more clever way to pass in the function and use it at compile time. Also here is the code that makes the list.

template <bool B, size_t Count,int ... Nums>                                       
struct generator;                                                                  

template <size_t Count>                                                            
struct generator<false,Count,0>                                                    
{                                                                                  
    constexpr static std::array<int,Count> array                                   
        = generator<false,Count,1,f(0)>::array;                                    
};                                                                                 

template <size_t Count, int Current, int ... Results>                              
struct generator<false,Count,Current, Results...>                                  
{                                                                                  
    constexpr static std::array<int,Count>  array                                  
        = generator<Current+1==Count,Count,Current+1,f(Current), Results...>::array;
};                                                                                 

template <size_t Count, int Current, int ... Results>                              
struct generator<true,Count,Current,Results...>                                    
{                                                                                  
    constexpr static std::array<int,Count>  array{{Results...}};                   
}; 

and before you ask no I don't actually have a real need for this.

As noted by @us2012 I should be specific about what I would rather have.

  1. nowrapping in namespace
  2. having to write the function but not actually passing it anywhere
  3. and not requiring the function to be named f
aaronman
  • 18,343
  • 7
  • 63
  • 78
  • When you say "a more clever way", which particular property of your current solution are you trying to avoid? – us2012 Oct 10 '13 at 01:05
  • @us2012 couple things, wrapping in namespace, having to write the function but not actually passing it anywhere and requiring the function to be named f – aaronman Oct 10 '13 at 01:06
  • The namespace doesn't seem to be necessary – sth Oct 10 '13 at 01:13
  • @sth if you want to reuse it with a different function it is – aaronman Oct 10 '13 at 01:15
  • 1
    Btw, [here's my solution to that problem](http://stackoverflow.com/a/19016627/420683). It does pass the function (as a pointer), though. – dyp Oct 10 '13 at 06:16
  • @DyP expanding the parameter pack with the function call is a way better solution I like it – aaronman Oct 10 '13 at 06:25

1 Answers1

2

You can actually use the function as a template parameter, here called Gen:

template <bool B, size_t Count, int Current, int Gen(size_t), int ... Nums>
struct generator;

template <size_t Count, int Current, int Gen(size_t), int ... Results>
struct generator<false,Count,Current, Gen, Results...>
{
    constexpr static std::array<int,Count>  array
         generator<Current+1==Count,Count,Current+1,Gen,
                   Gen(Current), Results...>::array;
};

template <size_t Count, int Current, int Gen(size_t), int ... Results>
struct generator<true,Count,Current,Gen,Results...>
{
    constexpr static std::array<int,Count>  array{{Results...}};
};

With this it actually works to pass a constexpr function to the template (if the type matches exactly):

// helper to hide the "internal" template parameters
template <size_t Count, int Gen(size_t)>
struct gen {                                                                 
   constexpr static std::array<int, Count> array = generator<false, Count, 0, Gen>::array;
};

constexpr int f(size_t i) { return i * i * i; }

int main()
{
    auto arr = gen<10,f>::array;
    for (auto i : arr) {
        std::cout << i << " ";
    }
    return 0;
}

You can also switch around the Gen(Current) and Results... parameters to get the values at the "correct" array indexes.

sth
  • 222,467
  • 53
  • 283
  • 367
  • This looks like it might work, the function takes an int though – aaronman Oct 10 '13 at 01:44
  • @aaronman: Like written here it only works if the function takes `size_t`, but it could probably be generalized by adding even another template parameter for the parameter type of the function, or so. – sth Oct 10 '13 at 01:46
  • I just meant because the one I wrote uses int – aaronman Oct 10 '13 at 01:47
  • Thanks, my problem from before is that I thought a function was not allowed to be a non-type template param – aaronman Oct 10 '13 at 01:58
  • 1
    @aaronman A function type is not allowed as type of a non-type template parameter, but a function type decays to a pointer-to-function type (even) in template parameters. And pointer-to-function (as well as reference to function) is allowed as type for a non-type template parameter. Additionally, integral, enumeration, lvalue reference, pointer-to-object, pointer-to-member and `nullptr_t` are allowed as types [temp.param]/4. – dyp Oct 10 '13 at 06:04
  • @DyP well you can't really pass around a function (other than std::function), so I mean function pointer before I was just too lazy to type it, however it did surprise me that it was allowed as a non type template param – aaronman Oct 10 '13 at 06:14