1

I am writing a physics simulation program and I want to do the following: I have a hana adapted struct and I want to check if this struct has member called "absorbedEnergy" at compile time using:

if constexpr ( ... )

What is the proper way of doing that in c++17 which I use?

Now using hana documentation I have came up with this:

struct HasAE { double absorbedEnergy };
struct HasNoAE {};

temaplate<typename Cell>
void irelevantFunction(Cell& cell){
    auto has_absorbedEnergy = hana::is_valid(
        [](auto &&p) -> decltype((void) p.absorbedEnergy) {});

    if constexpr(has_absorbedEnergy(cell)) { ... }
}

HasAE cell;
HasNoAE anotherCell;
cell.absorbedEnergy = 42; //value known at runtime

irelevantFunction(cell);
irelevantFunction(anotherCell);

The thing is that this compiles just fine with g++ 7.4.0 and does what I expect but fails to compile with clang++-8. It gives an error:

constexpr if condition is not a constant expression

I suspect that this originates from the fact that argument of has_absorbedEnergy - cell is not and constant expression. Is there a way around this?

Mike Spencer
  • 85
  • 2
  • 10
  • Yakk has working example of elevation from local variable to `constexpr` expression here, seems relevant: https://stackoverflow.com/questions/55975924/is-there-an-existing-name-for-this-type-and-function – R2RT May 07 '19 at 08:49
  • If you can pass `cell` by value and it has a constexpr copy constructor it would work. It's not clear if `cell` carries run-time state. – Jason Rice May 16 '19 at 15:40
  • As I understand it, cell has a default constexpr copy constructor, but carries run time state. I edited the question to specificaly show how I intend to use it. – Mike Spencer May 17 '19 at 16:23

1 Answers1

1

Your issue seems to be related to the requirement in the standard for the expession in if constexpr to be "contextually converted constant expression of type bool" (see this question). You can work around that by changing the if constexpr to:

if constexpr (decltype(has_absorbedEnergy(cell)){})

https://wandbox.org/permlink/hmMNLberLJmt0ueJ


Alternatively, you can use expression SFINAE to achieve what you want (see the cppreference.com documentation of std::void_t):

#include <type_traits>
#include <iostream>

template <typename, typename= std::void_t<>>
struct has_absorbedEnergy : std::false_type {};

template <typename T>
struct has_absorbedEnergy<T,
  std::void_t<decltype(std::declval<T&>().absorbedEnergy)>>
    : std::true_type {};

template <typename Cell>
void irelevantFunction([[maybe_unused]] Cell &cell) {
  if constexpr (has_absorbedEnergy<Cell>::value)
    std::cout << "Has absorbedEnergy\n";
  else
    std::cout << "Does not have absorbedEnergy\n";
}

struct HasAbsorbedEnergy
  { int absorbedEnergy; };

struct DoesNotHaveAbsorbedEnergy
  {};

int main()
{
HasAbsorbedEnergy Has;
DoesNotHaveAbsorbedEnergy DoesNot;

irelevantFunction(Has);
irelevantFunction(DoesNot);
}

https://wandbox.org/permlink/0559JhpVQBOwHC0Z

metalfox
  • 6,301
  • 1
  • 21
  • 43
  • Thank you very much. I wanted to avoid SFINAE as I don't find it very readable and I feel like almost any such problem should be solvable with constexpr if. That is why i asked the question in the first place. The trick with decltype(...){} works like a charm and the related question explains this pretty well. – Mike Spencer May 07 '19 at 15:08