Можно ли назначить и проверить структуру constexpr в C ++ 14? - PullRequest
2 голосов
/ 27 мая 2019

Я реализовал getop() в C ++ .Это класс со всеми видами звонков и свистков.

Один из этих звонков был бы способом проверки того, что пользователь вводит допустимые параметры во время компиляции.У меня уже есть часть этого для флагов (не показанных здесь), но я пытался заставить это работать для struct option, и в настоящее время у меня не было большого успеха.

Прежде всего, код позволяет мне определить массив опций, используя функцию define_option().Я хочу иметь возможность указывать поля структуры параметров в любом порядке и иметь некоторые проверки их работоспособности.Поэтому я использую список аргументов для шаблонной функции.Каждый аргумент является классом, который создается на месте, а затем define_option() выбирает правильный класс для каждого поля, извлекая значение.

Здесь все работает как шарм.Довольно простой C ++.

Проблема, с которой я сталкиваюсь, находится внутри класса define_option().Скажем, например, что я должен был попробовать следующее:

... define_option(...)
{
    ...
    option opt = { ... };
    static_assert(opt.f_short_name != '\0', "this failed");
    ...
}

Здесь я получаю ошибку.static_assert() говорит мне, что opt не является константой.

a.cpp: 168: 1: ошибка: непостоянное условие для статического утверждения

Итакмой шаг - пометить переменную opt как constexpr (что-то, что говорит ошибка, чтобы решить static_assert().)

... define_option(...)
{
    ...
    constexpr option opt = { ... };
    ...
}

Теперь, даже без static_assert(), код не 'т компилировать.Я получаю следующую ошибку:

a.cpp: 168: 5: ошибка: 'args # 0' не является константным выражением

Я думаю, что этона самом деле не выполнимо из функции define_option(), хотя можно проверить итоговый список опций, как показано в рабочем примере ниже.Таким образом, в итоге глобальная переменная opt является статической, полностью определенной во время компиляции (нет кода, необходимого для повторной работы над таблицей во время выполнения), но это не помогает мне, так как я не могу убедиться, что аргументы длякаждый вариант будет работать, как и ожидалось.

У меня есть онлайн-запущенный образец на колиру.

// compiled with: g++ -std=c++14 -o a ~/tmp/a.cpp
//
#include <iostream>

typedef char32_t            short_name_t;
constexpr short_name_t      NO_SHORT_NAME = L'\0';

struct option
{
    short_name_t        f_short_name = NO_SHORT_NAME;
    char const *        f_name = nullptr;
};


template<typename T, T default_value>
class OptionValue
{
public:
    typedef T   value_t;

    constexpr OptionValue<T, default_value>()
        : f_value(default_value)
    {
    }

    constexpr OptionValue<T, default_value>(T const v)
        : f_value(v)
    {
    }

    constexpr value_t get() const
    {
        return f_value;
    }

private:
    value_t     f_value;
};


class ShortName
    : public OptionValue<short_name_t, NO_SHORT_NAME>
{
public:
    constexpr ShortName()
        : OptionValue<short_name_t, NO_SHORT_NAME>()
    {
    }

    constexpr ShortName(short_name_t name)
        : OptionValue<short_name_t, NO_SHORT_NAME>(name)
    {
    }
};


class Name
    : public OptionValue<char const *, nullptr>
{
public:
    constexpr Name()
        : OptionValue<char const *, nullptr>()
    {
    }

    constexpr Name(char const * name)
        : OptionValue<char const *, nullptr>(name)
    {
    }
};


template<typename T, typename F, class ...ARGS>
constexpr typename std::enable_if<std::is_same<T, F>::value, typename T::value_t>::type find_option(F first, ARGS ...args)
{
    return first.get();
}


template<typename T, typename F, class ...ARGS>
constexpr typename std::enable_if<!std::is_same<T, F>::value, typename T::value_t>::type find_option(F first, ARGS ...args)
{
    return find_option<T>(args...);
}



template<class ...ARGS>
constexpr option define_option(ARGS ...args)
{
    option opt =
    {
        .f_short_name =          find_option<ShortName   >(args..., ShortName()),
        .f_name =                find_option<Name        >(args...),
    };

    return opt;
}


constexpr option const opt[] =
{
    define_option(
        Name("--help"),
        ShortName('h')
    )
};


// this test works as expected here
// but it does not work from inside define_option()
//
static_assert(opt[0].f_name[0] != '-', "Wrong start?!");

int main(int argc, char * argv [])
{

    std::cerr << "opt[0].f_short_name = " << opt[0].f_short_name << std::endl;

    return 0;
}

В настоящее время я ограничен C ++ 14.Если есть решение C ++ 17, мне все равно будет интересно.

1 Ответ

3 голосов
/ 27 мая 2019

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

template<class ...ARGS>
constexpr option define_option(ARGS ...args)
{
    option opt =
    {
        .f_short_name =          find_option<ShortName   >(args..., ShortName()),
        .f_name =                find_option<Name        >(args...),
    };
    if(/* check fails */) throw something;

    return opt;
}

Затем, когда вы попытаетесь выполнить

constexpr option const opt[] =
{
    define_option(
        Name("--help"),
        ShortName('h')
    )
};

, и проверка завершится неудачно, компилятор пожалуется, что этот вызов define_option не является константой.Выражение и не может использоваться для инициализации переменной constexpr.

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

...