@Mercury13
Программист на «си с крестами» и не только

Как сделать, чтобы конструктор при определённых условиях не компилировался — а не сваливал на runtime?

class Constant
{
private:
    int value;
public:
    constexpr Constant(int aValue) {
        if (aValue < 0) {
            throw std::invalid_argument("Bad value");
        } else {
            value = aValue;
        }
    }
};

Constant a(42);
Constant b(-10);


В таком случае переменная a проинициализируется при компиляции, а b при запуске (и выкинет аварию).
Да, в Си++20 есть constinit, но я хочу Си++17.

Точная задача: проинициализировать массив в стиле { key, value, key, value… }, и проконтролировать, что ключей ровно столько, сколько надо, и они не повторяются. По этой причине вызовы типа fromKeys<key1, key2>(value1, value2) не годятся.
  • Вопрос задан
  • 189 просмотров
Решения вопроса 1
@Mercury13 Автор вопроса
Программист на «си с крестами» и не только
Вот действующий код.
Хоть он на 20, простейшими define’ами (consteval → constexpr, constinit → пустота) можно собрать его и на 17.
#include <iostream>
#include <array>
#include <string>

enum class FromRawMem { INST };
enum class NoValue { INST };

#define consteval constexpr


class Char4
{
public:
    using CppArray = std::array<char, 4>;
    constexpr explicit Char4(NoValue) : fBuf { { 0, 0, 0, 0 } } {}
    consteval Char4(const char (&x)[5]) : fBuf { { x[0], x[1], x[2], x[3] } } {}
    constexpr Char4(const Char4&) = default;
    Char4(FromRawMem, const char* data) { fBuf.asInt = *reinterpret_cast<const uint32_t*>(data); }
    constexpr const CppArray& toAr() const { return fBuf.asAr; }
    CppArray& toAr () { return fBuf.asAr; }
    constexpr std::string_view toSv() const { return { fBuf.asAr.data(), 4 }; }

    /// @return  byte-order dependent integer value
    /// @warning
    ///    DO NOT save toRawInt() as Intel, Motorola or any numeral system (decimal etc).
    ///    You MAY save it as raw memory, or use it for optimization.
    constexpr uint32_t toRawInt() const { return fBuf.asInt; }
    constexpr const char* data() const { return fBuf.asAr.data(); }
private:
    union {
        std::array<char, 4> asAr;
        uint32_t asInt;
    } fBuf;
};

constexpr inline bool operator == (Char4 x, Char4 y) { return x.toRawInt() == y.toRawInt(); }
constexpr inline bool operator != (Char4 x, Char4 y) { return x.toRawInt() != y.toRawInt(); }
constexpr inline bool operator == (Char4 x, std::string_view y)
        { return y.size() == 4 && x.toRawInt() == *reinterpret_cast<const uint32_t*>(y.data()); }
constexpr inline bool operator != (Char4 x, std::string_view y)
        { return !operator == (x, y); }
constexpr inline bool operator == (std::string_view x, Char4 y)
        { return operator == (y, x); }
constexpr inline bool operator != (std::string_view x, Char4 y)
        { return !operator == (y, x); }


/// Size of enum class, no generic implementation.
template <class Ec>
constexpr size_t enumSize();

namespace detail {
    /// Jut does not compile if arguments contain assignments
    constexpr int enumDummy(...) { return 0; }

    template <typename T, std::size_t...Is>
    consteval std::array<T, sizeof...(Is)>
    make_array(const T& value, std::index_sequence<Is...>)
    {
        return {{(static_cast<void>(Is), value)...}};
    }

    template <std::size_t N, typename T>
    consteval std::array<T, N> make_array(const T& value)
    {
        return make_array(value, std::make_index_sequence<N>());
    }
}

#define DEFINE_ENUM_SIZE(Name) \
    template <> constexpr size_t enumSize<Name>() { return static_cast<int>(Name::NN); }

#define DEFINE_ENUM_N(Name, ...)  \
    enum class Name { __VA_ARGS__ };   \
    template <> constexpr size_t enumSize<Name>() { \
        enum Internal { __VA_ARGS__ , NNNNNN };  \
        detail::enumDummy(__VA_ARGS__);  \
        return NNNNNN; }


enum class DummyElem { INST };
enum class IncompleteArray { INST };
enum class FillArray { INST };
enum class PairwiseTempInit {INST };


namespace detail {
    namespace array {

        template <auto K, class V>
        class KeyValue {
        public:
            static constexpr auto index = K;
            V && v;
            consteval KeyValue(V && aV) : v(aV) {}
        };

        // checkOneForRepeat
        template <class Ec>
        consteval void checkOneForRepeat() {}

        template <class Ec, Ec Only>
        consteval void checkOneForRepeat() {}

        template <class Ec, Ec First, Ec Second, Ec ... Rest>
        consteval void checkOneForRepeat()
        {
            static_assert(First != Second, "Repeating array keys!");
            checkOneForRepeat<Ec, First, Rest...>();
        }

        // checkForRepeat
        template <class Ec>
        consteval void checkForRepeat() {}

        template <class Ec, Ec Only>
        consteval void checkForRepeat() {}

        template <class Ec, Ec First, Ec Second, Ec ... Rest>
        consteval void checkForRepeat()
        {
            checkOneForRepeat<Ec, First, Second, Rest...>();
            checkForRepeat<Ec, Second, Rest...>();
        }

        template <class Ec, Ec ... args>
        consteval void checkKeys()
        {
            checkForRepeat<Ec, args...>();
        }

    }
}

template<auto K, class V>
consteval auto kv(V && v) { return detail::array::KeyValue<K, V>(v); }


template <class T, class Ec>
class EcArray
{
public:
    static constexpr auto Size = enumSize<Ec>();
    using CppArray = std::array<T, Size>;
    using Elem = T;
    using iterator = Elem*;
    using const_iterator = const Elem*;
    constexpr CppArray& toAr() { return fBuf; }
    constexpr const CppArray& toAr() const { return fBuf; }
    constexpr Elem* data() { return fBuf.data(); }
    constexpr const Elem* data() const { return fBuf.data(); }
    constexpr size_t size() const { return Size; }
    constexpr iterator begin() { return fBuf.begin(); }
    constexpr iterator end() { return fBuf.end(); }
    constexpr const_iterator begin() const { return fBuf.begin(); }
    constexpr const_iterator end() const { return fBuf.end(); }
    constexpr const_iterator cbegin() const { return fBuf.begin(); }
    constexpr const_iterator cend() const { return fBuf.end(); }

    constexpr EcArray() = delete;
    constexpr EcArray(NoValue) {}
    constexpr EcArray(const EcArray&) = default;
    constexpr EcArray(EcArray&&) = default;

    EcArray& operator = (const EcArray&) = default;
    EcArray& operator = (EcArray&&) = default;

    template <class U>
    constexpr EcArray(NoValue, const U&& x) : fBuf {x } {}

    template <class ... Args>
    consteval EcArray(Args&& ... x)
        : fBuf { x... }
        { static_assert(sizeof...(Args) == Size, "EcArray size mismatch"); }

    template <class U, Ec K, class V, class ... Args>
    consteval EcArray(
            PairwiseTempInit, U&& temp,
            detail::array::KeyValue<K, V> first,
            Args&& ... rest)
        : fBuf { detail::make_array<Size, T>(temp) }
    {
        static_assert(sizeof...(Args) + 1 == Size, "EcArray pairwise size mismatch");
        detail::array::checkKeys< Ec, K, (std::remove_reference_t<decltype(rest)>::index)... >();
        initPairwise(first, rest...);
    }

private:
    CppArray fBuf;

    consteval void initPairwise() {}

    template <Ec K, class V, class ... Args>
    consteval void initPairwise(
            detail::array::KeyValue<K, V> first,
            Args&& ... rest)
    {
        fBuf[static_cast<size_t>(K)] = T { first.v };
        initPairwise(rest...);
    }
};


DEFINE_ENUM_N( Letter, A, B, C )

extern const EcArray <Char4, Letter> names2;

//const EcArray <Char4, Letter> names { "alph", "brav", "char" };

constinit const EcArray <Char4, Letter> names2
    { PairwiseTempInit::INST, Char4 { NoValue::INST},
      kv <Letter::A> ( "alp1" ),
      kv <Letter::B> ( "bra1" ),
      kv <Letter::C> ( "cha1" ) };


int main()
{
    std::cout << "Hello World!" << std::endl;
    return 0;
}
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы