3

I have the following problem: I have a class hierarchy with a base class and two sub-classes. I have implemented a resolve_type function that accepts an instance of the base class and a generic lambda (or similar). The function resolves its type and passes it to the lambda. Inside this lambda, I’d like to check the column’s type within a constexpr-if condition in order to exclude certain types. I have tried to do this with constexpr member functions in the sub-classes, which unfortunately didn’t work.

Code:

class AbstractColumn
{
};

template <typename Type>
class DataColumn : public AbstractColumn
{
public:
    constexpr bool is_reference_column() { return false; }

    void foo() {}
};

class ReferenceColumn : public AbstractColumn
{
public:
    constexpr bool is_reference_column() { return true; }
};

template <typename Functor>
resolve_type(const AbstractColumn & col, const Functor & func);

Usage:

AbstractColumn & col = ...;

...

resolve_type(col, [] (const auto & col)
{
    // col could be ReferenceColumn, DataColumn<int>, DataColumn<float>, DataColumn<double>, DataColumn<std::string> ...
    if constexpr (!col.is_reference_column()) {
        col.foo();
    }
});

Compiler Error:

Apple LLVM version 8.1.0 (clang-802.0.42)
error: constexpr if condition is not a constant expression 
if constexpr (col.is_reference_column()) {

I know that I could use decltype to get the type and then continue using some template magic, but I had hoped to find something that is a bit more readable. My project already uses boost and its hana library, so solutions could also use these two. Does anyone have any ideas?

skypjack
  • 49,335
  • 19
  • 95
  • 187
maxj
  • 45
  • 4
  • I like the first idea more since I'm planning to define a few more functions. But are you sure you can do `decltype(col)::value` on a reference value? – maxj Jul 22 '17 at 19:59
  • Added a few other alternatives to the answer. – skypjack Jul 22 '17 at 20:19

2 Answers2

3

Use a static constexpr method instead.
It follows a minimal, working example:

#include<type_traits>

struct A {
    static constexpr bool is_reference_column() { return false; }
};

int main() {
    [](const auto &col) {
        if constexpr(std::decay_t<decltype(col)>::is_reference_column()) {
            // ...
        }
    }(A{});
}

Or just inherits from std::true_type/std::false_type:

#include<type_traits>

struct A: std::true_type {};

int main() {
    [](const auto &col) {
        if constexpr(std::decay_t<decltype(col)>::value) {
            // ...
        }
    }(A{});
}

Or use an intermediate class template instead of redefining continuously is_reference_column:

#include<type_traits>

template<bool refcol>
struct I {
    static constexpr bool is_reference_column = refcol;
};

struct A: I<true> {};

int main() {
    [](const auto &col) {
        if constexpr(std::decay_t<decltype(col)>::is_reference_column) {
            // ...
        }
    }(A{});
}

Plenty of alternatives, but you cannot simply use col in a constant expression just because you declared it as a const reference. col is a runtime instance of a type T, there is no chance you can use it at compile-time as you tried to do.

skypjack
  • 49,335
  • 19
  • 95
  • 187
1

I think you're overthinking this. You don't need a member function to just identify the type of the object. You can just look at the type. So at a first go, that's simply:

resolve_type(col, [] (const auto& col)
{
    if constexpr (hana::typeid_(col) == hana::type_c<ReferenceColumn>) {
        col.foo();
    }
});

Even simpler would just be to create an overload set and just use overload resolution. There are several implementations of such a mechanism floating around here, it's especially straightforward to write in C++17. Using that:

resolve_type(col, overload(
    [](ReferenceColumn const& ref){
       ref.foo();
    },
    [](auto const& other) {
    }));
Barry
  • 286,269
  • 29
  • 621
  • 977
  • Hi. The first code block doesn’t compile, because `col` is not a constant expression. The second idea sounds interesting. I’ll have a look. – maxj Jul 27 '17 at 15:52