2

I am looking for a way to pass a generic (constexpr, obviously) function to a template. It has to be able to take any amount of parameters, without using a lambda. This is what I have so far:

template<typename T, T(*FUNC)()> struct CALL
{
    static inline constexpr decltype(FUNC()) EXEC()
    {
        return FUNC();
    }
};

This however only works if the passed function takes no parameters. Is there a way to make the template accept ANY constexpr function? Passing a std::function does not seem to work. I suppose the key is variadic template parameters, but I have no idea how to take advantage of them in this situation.

Byzantian
  • 3,208
  • 4
  • 27
  • 31
  • 1
    Why the `constexpr` hangup? Why would you think it's relevant/necessary? – ildjarn Jan 12 '13 at 21:11
  • You can't possibly allow callers to pass in *any* function and expect your code to do anything useful with it. There has to be some restrictions in place. Are you prepared to call functions that requires 20 arguments? How about functions that require a platform-specific data type? What you're asking creates more problems than it solves. – In silico Jan 12 '13 at 21:11
  • @In silico Why not? All I would have to do is to pass the parameters to the template as well. I just don't know how. @ ildjarn What do you mean by "hangup"? – Byzantian Jan 12 '13 at 21:12
  • @cyberpunk_: ildjarn is asking why `constexpr` is relevant to your question. – In silico Jan 12 '13 at 21:14
  • Try `template struct CALL { ...` – n. m. could be an AI Jan 12 '13 at 21:15
  • @cyberpunk_: So you want your callers to also pass in the required arguments? (That is not clear from your question, by the way.) Do you intend to store these arguments so you can call the function at a later point? – In silico Jan 12 '13 at 21:16
  • @In silico because the function has to be evaluated during compile-time. Passing a non-constexpr function to a template wouldn't work, would it? – Byzantian Jan 12 '13 at 21:16
  • 3
    @cyberpunk_: Do you want thefunction to be called at compile-time? You're passing the function pointer as a non-type template parameter, so the pointer is already available at compile time, and hence `constexpr` is not required. It will still be *called* at runtime. However, what scenario do you think this will be useful for? I have a feeling that you're asking about a possible solution and not your actual problem. – In silico Jan 12 '13 at 21:18
  • @In silico You are correct. I am looking for a way to force a constexpr to be evaluated during compile-time in one line, while still allowing floating point parameters. I already have a solution for this, but I would rather describe it as a sloppy hack. The cleaner solution only works with integers. Look at this thread to see what I am rambling on about. http://stackoverflow.com/questions/14294271/forcing-a-constant-expression-to-be-evaluated-during-compile-time – Byzantian Jan 12 '13 at 21:25
  • @cyberpunk_: Have you checked what the compiler is doing without this extra code? You asked when is it guaranteed that the `constexpr` is evaluated at compile time, and that guarantee is only limited to cases where the value is needed as a `constexpr` itself, but that does not mean that in all other cases it won't be evaluated at compile time. In particular the `POW` example in the linked question *should* be evaluated at compile time in any sane compiler. – David Rodríguez - dribeas Jan 12 '13 at 22:41
  • @cyberpunk_, your new approach doesn't help anything at your aim, you have just chained a call to another `constexpr` function, you're going to chain a call AND the rules, in the end you will end up going to initialize a `constexpr` variable to acquire your guarantees. – oblitum Jan 13 '13 at 21:42

2 Answers2

7

If I understand correctly what you are trying to achieve, you can use a template function rather than a template class with a static function:

#include <iostream>

template<typename T, typename... Ts>
constexpr auto CALL(T (*FUNC)(Ts...), Ts&&... args) -> decltype(FUNC(args...))
{
    return FUNC(std::forward<Ts>(args)...);
}

constexpr double sum(double x, double y)
{
    return (x + y);
}

int main()
{
    constexpr double result = CALL(sum, 3.0, 4.0);
    static_assert((result == 7.0), "Error!");
    return 0;
}
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • Thanks, but do I really have to use a constepxr function? The whole idea behind this is to force a constexpr function to be evaluated during compile-time. The reason why I was using a template class, is because templates MUST be evaluated at compile time, while constexpr function are also runtime callable. So, is there a way to implement your solution only using a template class? – Byzantian Jan 12 '13 at 22:28
  • 1
    I think you are making some confusion when you say that templates must be "evaluated" at compile-time. templates are "instantiated" at compile-time, which is something different. no code gets *executed* during instantiation, only *generated* – Andy Prowl Jan 12 '13 at 22:49
  • @cyberpunk_: so yes you have to use a constexpr function if you want some compile-time code *execution* (= evaluation) to be performed. – Andy Prowl Jan 12 '13 at 22:51
  • @AndyProwl I know what he's trying to achieve, it's not that, he want template non-type parameters being passed as arguments to a function which the return goes to a constexpr before getting it, all to enforce compile-time code execution (=evaluation) of a constexpr function in a shorter form than by initializing a constexpr variable with the return of the function. He is chasing his own tail on that =D http://stackoverflow.com/q/14294271/1000282 – oblitum Jan 13 '13 at 16:33
  • @chico: I trust you, but I don't understand your explanation either :) anyway, my point is that during template instantiation there's no code being *executed*, not even code of a `constexpr` function. template meta-programming does allow to do some *computation* in the form (rather, in the process) of code generation, but that's like another language, more similar to functional programming than to regular procedural programming. and in any case it won't *execute* the body of any function passed in as a template argument, not even `constexpr` ones – Andy Prowl Jan 13 '13 at 16:36
  • @AndyProwl it could instantiate a template just with the purpose of generating a member to call the constexpr function. or something like that I'm not sure... I guess he wanted to use the feature of the non-type template parameters are assured to be compile-time entities. – oblitum Jan 13 '13 at 16:42
  • @chico: but even if he instantiates a template and generates a member (let's say, `foo()`) which calls a `constepxr` function, the mere fact of *generating* the `foo()` member doesn't mean its body will be *executed*, including the call to the `constexpr` function. – Andy Prowl Jan 13 '13 at 16:49
  • @AndyProwl, I thought of this simple illustration, `template struct caller { operator int() {return F(I);} }; constexpr int f(int i) {return i;} int main() { int x = caller(); }` I've dealt just with the argument stuff, and just one. – oblitum Jan 13 '13 at 16:51
  • @chico: but that won't *execute* F(I) at compile-time, it will just generate the `operator int()` function... – Andy Prowl Jan 13 '13 at 17:00
  • @AndyProwl I've dealt just with the part of ensuring arguments can only be compile-time values. It has generated an `operator int()` function with pinned constants. – oblitum Jan 13 '13 at 17:01
  • @AndyProwl, this one is better, now `z` can or not be `constexpr`, `template struct caller { static constexpr int x = F(I); constexpr operator int() {return x;} }; constexpr int f(int i) {return i;} int main() { constexpr int z = caller(); }` – oblitum Jan 13 '13 at 17:15
0
template<int... I>
struct with
{
    template<int F(decltype(I)...)>
    struct call
    {
        static constexpr int value = F(I...);
    };
};

constexpr int f(int i) {return i;}
constexpr int g(int i, int j) {return i + j;}

int main()
{
    int u = with<0>::call<f>::value;
    constexpr int v = with<0, 1>::call<g>::value;
}

Note, this has some of the limitations as in your previous question which I answer with std::integral_constant. But a constexpr double value could still be generated from non-type template arguments at compilation time.

#include <iostream>

template<typename T, T... v>
struct with
{
    template<typename R, R f(decltype(v)...)>
    struct call
    {
        static constexpr R value = f(v...);
    };
};

#define AT_COMPILATION(f, x, ...) with<decltype(x), x, ##__VA_ARGS__>::call<decltype(f(x, ##__VA_ARGS__)), f>::value

constexpr long f(long i) {return i;}
constexpr double g(int i, int j) {return static_cast<double>(i) / j;}

int main()
{
    constexpr double u = with<long, 0L>::call<decltype(f(0L)), f>::value;

    std::cout << with<int, 5, 2>::call<double, g>::value << std::endl;

    constexpr double v = AT_COMPILATION(f, 0L);

    std::cout << AT_COMPILATION(g, 5, 2) << std::endl;
}
Community
  • 1
  • 1
oblitum
  • 11,380
  • 6
  • 54
  • 120