std::is_constant_evaluated()
От: Videoman Россия https://hts.tv/
Дата: 13.04.20 15:06
Оценка: 8 (1)
Вопрос к знатокам С++17/20. Возможно ли как-то реализовать в С++17. Если нет, то как обойти в таком случае (псевдокод):
constexpr int add(int a, int b) 
{
  if (std::is_constant_evaluated())
    return a + b;
  else
    __intrinsic_add(a, b); 
}

Буду благодарен за советы и рекомендации?

P.S. Желательно под MSVS 2017, v.141
Отредактировано 13.04.2020 15:10 Videoman . Предыдущая версия .
Re: std::is_constant_evaluated()
От: watchmaker  
Дата: 13.04.20 17:00
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Возможно ли как-то реализовать в С++17.


Требуется же поддержка в компиляторе. Например, через __builtin_is_constant_evaluated как в gcc и clang.

V> Если нет, то как обойти в таком случае?


Пока только дать программисту две функции и попросить самому выбрать нужную:
constexpr int constexpr_add(int a, int b);

int runtime_add(int a, int b);
Отредактировано 13.04.2020 17:01 watchmaker . Предыдущая версия .
Re[2]: std::is_constant_evaluated()
От: Videoman Россия https://hts.tv/
Дата: 13.04.20 17:34
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Требуется же поддержка в компиляторе. Например, через __builtin_is_constant_evaluated как в gcc и clang.

С одной стороны я догадывался, а с другой эта фича задекларирована как расширение библиотеки. По-этому была надежда, вдруг это можно с помощью шаблонной магии разрулить и я что-то упускаю.

W>Пока только дать программисту две функции и попросить самому выбрать нужную:

W>
W>constexpr int constexpr_add(int a, int b);

W>int runtime_add(int a, int b);
W>

К сожалению не получается, т.к. у меня может быть куча вложенных constexpr-методов или конструкторов и где-то внутри вот такой вот constexpr_add/runtime_add. Т.е. придется дублировать разные методы, а главное классы выше по иерархии, и так как методы с constexpr и без нельзя перегрузить, код перестанет компилироваться одно с другим.
Отредактировано 13.04.2020 17:46 Videoman . Предыдущая версия . Еще …
Отредактировано 13.04.2020 17:45 Videoman . Предыдущая версия .
Re[3]: std::is_constant_evaluated()
От: rg45 СССР  
Дата: 16.04.20 13:54
Оценка:
Здравствуйте, Videoman, Вы писали:

V>К сожалению не получается, т.к. у меня может быть куча вложенных constexpr-методов или конструкторов и где-то внутри вот такой вот constexpr_add/runtime_add. Т.е. придется дублировать разные методы, а главное классы выше по иерархии, и так как методы с constexpr и без нельзя перегрузить, код перестанет компилироваться одно с другим.


А не получится ли параметризовать все дерево вычислений режимом (компайл- ран- тайи)? Как-то так:

http://coliru.stacked-crooked.com/a/9c8a79c7c26b2f20

#include <iostream>

int __intrinsic_add(int a, int b) { std::cout << "intrinsic_add: " << std::endl; return a + b; }

enum class ComputationMode {
    RunTime,
    CompileTime,
};

template <ComputationMode>
int add(int a, int b) { return __intrinsic_add(a, b); }

template <>
constexpr int add<ComputationMode::CompileTime>(int a, int b) { return a + b; }

template <ComputationMode mode>
constexpr int compute(int a, int b) { return add<mode>(a, b); }

int main()
{
    std::cout << compute<ComputationMode::RunTime>(2, 3) << std::endl;
    std::cout << compute<ComputationMode::CompileTime>(2, 3) << std::endl;
}
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[4]: std::is_constant_evaluated()
От: Videoman Россия https://hts.tv/
Дата: 16.04.20 15:55
Оценка: +1
Здравствуйте, rg45, Вы писали:

R>А не получится ли параметризовать все дерево вычислений режимом (компайл- ран- тайи)? Как-то так:


Не уверен что мне подойдет, но в плане обсуждения идеи: я вижу что у вас все-равно явно придется параметризовать ComputationMode::RunTime или ComputationMode::CompileTime, а мне нужно делать это автоматически, т.к. в случае compile-time в С++17 нельзя использовать instrinsic, а в случае runtime — как-раз эффективнее использовать instrinsic. Я не вижу здесь автоматизации выбора той или иной реализации в зависимости от того в каком режиме собирается код.
Re: std::is_constant_evaluated()
От: Vain Россия google.ru
Дата: 18.04.20 11:31
Оценка:
Здравствуйте, Videoman, Вы писали:

V>P.S. Желательно под MSVS 2017, v.141

Да будут же жить макросы вечно! (спасибо последнему стандарту)
  https://godbolt.org/z/fWMFKV
#include <type_traits>
#include <cstdio>

#define UTILITY_CONSTEXPR(exp)                          (::utility::constexpr_bool<(exp) ? true : false>::value)

#ifndef __clang__
#define UTILITY_IS_CONSTEXPR_VALUE(...)                 UTILITY_CONSTEXPR(noexcept(::utility::makeprval((__VA_ARGS__, 0))))
#else
#define UTILITY_IS_CONSTEXPR_VALUE(...)                 UTILITY_CONSTEXPR(__builtin_constant_p((__VA_ARGS__, 0)))   // can be used for the GCC too
#endif

namespace utility
{
    // to suppress `warning C4127: conditional expression is constant`
    template <bool B, typename...>
    struct constexpr_bool
    {
        static constexpr const bool value = B;
    };

    template <bool B, typename... types>
    const bool constexpr_bool<B, types...>::value;

    // remove_reference + remove_cv
    template <typename T>
    struct remove_cvref
    {
        using type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
    };

    // CAUTION:
    //  Return values in the `makeprval` are required to avoid breakage in the `Visual Studio 2015 Update 3` compiler.
    //
    template <typename T>
    constexpr typename remove_cvref<T>::type makeprval(T && v)
    {
        return v;
    }

    // static array type must be overloaded separately, otherwise will be an error: `error: function returning an array`
    template <typename T>
    constexpr const typename remove_cvref<T>::type & makeprval(const T & v)
    {
        return v;
    }
}

inline int myintrinsic_add(int a, int b)
{
    return a + b + 1;
}

template <bool is_constexpr>
struct t_optimized_add
{
    constexpr int operator()(int a, int b) const
    {
        return a + b;
    }
};

template <>
struct t_optimized_add<false>
{
    int operator()(int a, int b) const
    {
        return myintrinsic_add(a, b);
    }
};

#define OPTIMIZED_ADD(a, b) \
    t_optimized_add<UTILITY_IS_CONSTEXPR_VALUE(t_optimized_add<true>()(a, b))>()(a, b)

static int a = 100;

int main()
{
    printf("%d\n", OPTIMIZED_ADD(1, 1));
    printf("%d\n", OPTIMIZED_ADD(a, 1));
}
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Отредактировано 18.04.2020 11:33 Vain . Предыдущая версия .
Re[2]: std::is_constant_evaluated()
От: Videoman Россия https://hts.tv/
Дата: 18.04.20 14:20
Оценка:
Здравствуйте, Vain, Вы писали:

V>...


Я правильно понимаю что здесь вес финт в том, что noexcept даст true для constexpr ?
Re[3]: std::is_constant_evaluated()
От: Vain Россия google.ru
Дата: 18.04.20 14:27
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Я правильно понимаю что здесь вес финт в том, что noexcept даст true для constexpr ?

https://en.cppreference.com/w/cpp/language/noexcept

The noexcept operator performs a compile-time check that returns true if an expression is declared to not throw any exceptions.

Любое не констекспр выражение может потенциально кинуть эксепшен.
Под "is declared to not throw any exceptions" имеется ввиду и просто код или выражение, которое никто заранее конечно не декларировал как expect или noexcept.
Отсюда, видимо, и танцы.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[4]: std::is_constant_evaluated()
От: Videoman Россия https://hts.tv/
Дата: 18.04.20 15:29
Оценка:
Здравствуйте, Vain, Вы писали:

V>Любое не констекспр выражение может потенциально кинуть эксепшен.

V>Под "is declared to not throw any exceptions" имеется ввиду и просто код или выражение, которое никто заранее конечно не декларировал как expect или noexcept.
V>Отсюда, видимо, и танцы.

Ну как-то не очень надежно. А если декларировал?
Re[5]: std::is_constant_evaluated()
От: Vain Россия google.ru
Дата: 19.04.20 13:41
Оценка: 6 (1)
Здравствуйте, Videoman, Вы писали:

V>>Любое не констекспр выражение может потенциально кинуть эксепшен.

V>>Под "is declared to not throw any exceptions" имеется ввиду и просто код или выражение, которое никто заранее конечно не декларировал как expect или noexcept.
V>>Отсюда, видимо, и танцы.
V>Ну как-то не очень надежно. А если декларировал?
А в чём проблема?
  https://godbolt.org/z/FpTYNz
#include <type_traits>
#include <cstdio>

#define UTILITY_CONSTEXPR(exp)                          (::utility::constexpr_bool<(exp) ? true : false>::value)

#ifndef __clang__
#define UTILITY_IS_CONSTEXPR_VALUE(...)                 UTILITY_CONSTEXPR(noexcept(::utility::makeprval((__VA_ARGS__, 0))))
#else
#define UTILITY_IS_CONSTEXPR_VALUE(...)                 UTILITY_CONSTEXPR(__builtin_constant_p((__VA_ARGS__, 0)))   // can be used for the GCC too
#endif

namespace utility
{
    // to suppress `warning C4127: conditional expression is constant`
    template <bool B, typename...>
    struct constexpr_bool
    {
        static constexpr const bool value = B;
    };

    template <bool B, typename... types>
    const bool constexpr_bool<B, types...>::value;

    // remove_reference + remove_cv
    template <typename T>
    struct remove_cvref
    {
        using type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
    };

    // CAUTION:
    //  Return values in the `makeprval` are required to avoid breakage in the `Visual Studio 2015 Update 3` compiler.
    //
    template <typename T>
    constexpr typename remove_cvref<T>::type makeprval(T && v)
    {
        return v;
    }

    // static array type must be overloaded separately, otherwise will be an error: `error: function returning an array`
    template <typename T>
    constexpr const typename remove_cvref<T>::type & makeprval(const T & v)
    {
        return v;
    }
}

inline int myintrinsic_add(int a, int b)
{
    return a + b + 1;
}

template <bool is_constexpr>
struct t_optimized_add
{
    constexpr int operator()(int a, int b) const
    {
        return a + b;
    }
};

template <>
struct t_optimized_add<false>
{
    int operator()(int a, int b) const
    {
        return myintrinsic_add(a, b);
    }
};

#define OPTIMIZED_ADD(a, b) \
    t_optimized_add<UTILITY_IS_CONSTEXPR_VALUE(t_optimized_add<true>()(a, b))>()(a, b)

constexpr int foo1() noexcept
{
    return 0;
}

constexpr int foo2() throw(int)
{
    return 0;
}

int foo3() throw(int)
{
    return 0;
}

int foo4()
{
    try {
        throw 0;
    }
    catch(...)
    {
    }
    return 0;
}

int foo5()
{
    return 0;
}

int main()
{
    printf("%d\n", OPTIMIZED_ADD(foo1(), 1));
    printf("%d\n", OPTIMIZED_ADD(foo2(), 1));
    printf("%d\n", OPTIMIZED_ADD(foo3(), 1));
    printf("%d\n", OPTIMIZED_ADD(foo4(), 1));
    printf("%d\n", OPTIMIZED_ADD(foo5(), 1));
}

1
1
2
2
2
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[6]: std::is_constant_evaluated()
От: Videoman Россия https://hts.tv/
Дата: 19.04.20 20:33
Оценка:
Здравствуйте, Vain, Вы писали:

V>А в чём проблема?


Да нет проблем. Просто с детства не люблю макросы и то как они "протекают" между пространствами имен.
Re[7]: std::is_constant_evaluated()
От: Vain Россия google.ru
Дата: 20.04.20 00:20
Оценка: +1
Здравствуйте, Videoman, Вы писали:

V>>А в чём проблема?

V>Да нет проблем. Просто с детства не люблю макросы и то как они "протекают" между пространствами имен.
Ну так используй новый стандарт.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.