themagicalyang
themagicalyang

Reputation: 2553

How to use if-constexpr without repeating code?

Currently I am doing:

if constexpr(constexpr_bool_var1) {
    auto arg1 = costly_arg1(); auto arg2 = costly_arg2();
    if (costly_runtime_function(arg1, arg2)) {
        // do X, possibly more constexpr conditions
        // do Y
        // ...
    }
} else {
    // do X, possibly more constexpr conditions
    // do Y
    // ...
}

One possible way is to convert the do X/Y etc. to one function doXY() and call it in both places, however it seems very unwieldy, as I have to write a function that solely exists for convenience of meta programming.

What I want is something like:

if not constexpr(constexpr_bool_var1 && some_magic(costly_runtime_function(arg1, arg2)) {
  // do X, do Y
} 

Another way is:

auto arg1 = costly_arg1(); // Unneeded extra work out not within constexpr
auto arg2 = costly_arg2();
if (constexpr_bool_var1 && costly_runtime_function(arg1, arg2)) {
} else {
    // do X, possibly more constexpr conditions
    // do Y
    // ...
}

However here arg1 and arg2 are being declared outside the if condition so they will be needlessly instantiated.

Upvotes: 4

Views: 134

Answers (3)

Guillaume Racicot
Guillaume Racicot

Reputation: 41760

You could use a lambda for that:

auto stuff = [&] {
    // do X, possibly more constexpr conditions
    // do Y
    // ...
};

if constexpr(constexpr_bool_var1) {
    auto arg1 = costly_arg1(); auto arg2 = costly_arg2();
    if (costly_runtime_function(arg1, arg2)) {
        stuff();
    }
} else {
    stuff();
}

And if your lambda can receive auto values, you could also pass variable of different types from inside the if scope.

Upvotes: 0

Daniel H
Daniel H

Reputation: 7433

I’m not sure I understand your question correctly; your original code and your second alternative don’t express quite the same function (the meaning of costly_runtime_function is reversed from “do X and Y” to “don’t do X and Y”), and in your first propoed alternative I don’t understand what your proposed syntax or what some_magic is. I’m answering your question with the semantics of your original code sample.

The best way to handle this is probably with a flag for whether or not to do X and Y:

bool do_x_y = true;
if constexpr(constexpr_bool_var1) {
    // Maybe we don't actually want X and Y
    auto arg1 = costly_arg1(); auto arg2 = costly_arg2();
    do_x_y = costly_runtime_function(arg1, arg2);
}
if (do_x_y)  {
    // do X, possibly more constexpr conditions
    // do Y
    // ...
}

Note that, as Andrei R. points out in the comments, the compiler can probably handle the optimization anyway. This makes it clearer to a human reader that you want this handled at compile time, though.

Upvotes: 3

Richard Hodges
Richard Hodges

Reputation: 69864

Here's one way. Whether it's more expressive/maintainable I cannot say.

#include <cstdlib>
#include <utility>

/*
 * simulation
 */
void doX() {}
void doY() {}

int costly_arg1() { return 1; }
int costly_arg2() { return 2; }

bool costly_runtime_function(int, int) { return rand() < RAND_MAX / 2; }

constexpr bool constexpr_bool_var1 = true;

/*
 * A functor that maybe does something
 */
template<class F>
struct maybe_do
{
    constexpr maybe_do(F&& f) : f(std::move(f)) {}

    constexpr void operator()() const 
    {
        if (enabled)
            f();
    }

    constexpr void enable(bool e) {
        enabled = e;
    } 

    F f;
    bool enabled = true;
};

int main()
{
    auto thing = maybe_do{
        [] {
            doX();
            doY();
        }
    };

    if constexpr(constexpr_bool_var1) 
    {
        thing.enable(costly_runtime_function(costly_arg1(), 
                                             costly_arg2()));
    }
    thing();
}

Upvotes: 0

Related Questions