Сократить аргумент функции-члена, ожидающей введенный тип (перечислимый класс) - PullRequest
0 голосов
/ 25 февраля 2019

TL; DR Существует ли более короткий синтаксис для аргумента типа перечисления для функции-члена (field_inst.write(decltype(field_inst)::Type::cpr1_4096);) в следующем коде?

namespace Hal {
// complex template definition `Bit_Field`
template<
    class Tregister,
    typename Tregister::Data Toffset,
    typename Tregister::Data Tmask,
    class Tfield_type,
    class Tmutability_policy = typename Tregister::Mutability_Policy
>
struct Bit_Field : Tregister {
    using Type = Tfield_type;

    static Field read()
    { // ... reading ...
    }
};

namespace Aeat_8800_Q24 {
enum class {cpr1_4096 = 0x5,
 // ... more settings ...
};
} // namespace Aeat_8800_Q24

} // namespace HAl

int main(void) {
  // this template is used multiple times, different template arguments
  // on each instantiation (using / typedef not practical)
  Hal::Bit_Field<reg<0x8FA>, 0x0, 0x7, Hal::Aeat_8800_Q24::Cpr_Setting1>
        field_inst;

  // QUESTION: How can I write that more pleasingly?
    field_inst.write(decltype(field_inst)::Type::cpr1_4096);
    field_inst.write(Hal::Aeat_8800_Q24::Cpr_Setting1::cpr1_4096);
}

Отказ от ответственности: Сам вопрос является дубликатом: Как предотвратить квалификацию класса при использовании вложенного перечислимого класса в аргументах функции-члена .

Однако я хочу знать, были ли улучшения с 2016 (дата вопроса) / C ++ 11 , которые облегчили бы использование библиотеки (более приятный синтаксис).

1 Ответ

0 голосов
/ 26 февраля 2019

Отказ от ответственности

Решение, представленное в этом ответе, предназначено для удовлетворения первоначальной потребности: написание более короткого, но выразительного клиентского кода.При этом я пойду на большие ненужные длины.Для меня рекомендуемым поведением является использование звуковых using объявлений, таких как:

int main() {
    using Hal::Aeat_8800_Q24::Cpr_Setting1;
    // Or if enums are alone and well encapsulated in their namespace:
    //using namespace Hal::Aeat_8800_Q24;
    Hal::Bit_Field<reg<0x8FA>, 0x0, 0x7, Cpr_Setting1>
        field_int;
    field_int.write(Cpr_Setting1::cpr1_4096);
    // ...
}

Решение по избыточному количеству ресурсов

Вы можете разработать (очень усиленное) решение на основе пользовательскихлитералы.

// This is just a little helper for later
namespace literals {

template <typename T, T... Cs>
constexpr auto operator ""_as_iseq() {
    return std::integer_sequence<T, Cs...>{};
}

}

Затем начинается самое интересное.Объявите класс черты, подобный этому, вместе с псевдонимом помощника:

// Inside namespace Hal::Aeat_8800_Q24
template <typename T> struct setting_enum;
template <typename T>
using setting_enum_t = typename setting_enum<T>::type;

Затем специализируйте его для каждого из ваших перечислений:

// (Still) Inside namespace Hal::Aeat_8800_Q24
using namespace literals;

template <>
struct SettingEnum<decltype("Cpr_Setting1"_as_iseq)> {
    using type = Cpr_Setting1;
};

Наконец, давайте определим последний литеральный оператор

// Inside namespace Hal::Aeat_8800_Q24
namespace settings_literals {

template <typename T, T... Cs>
constexpr auto operator""_s()
    -> setting_enum_t<
        std::integer_sequence<T, Cs...> >;
}

Теперь ваш код клиента просто должен сделать это:

using namespace Hal::Aeat_8800_Q24::settings_literals;
// ...
field_inst.write(decltype("Cpr_Setting1"_s)::cpr1_4096);

Это все еще довольно долго ... Есть ли способ сделать лучше?Да, действительно ... Вместо того, чтобы использовать вышеописанную черту, давайте вместо этого использовать шаблон переменной.

// In namespace Hal
namespace enum_traits {
using namespace literals;

template <typename Enum, typename ValueIntSeq>
constexpr void *ENUM_VALUE = nullptr;

template <>
constexpr Aeat_8800_Q24::Cpr_Setting1 ENUM_VALUE<
    Aeat_8800_Q24::Cpr_Setting1, decltype("cpr1_4096"_as_iseq)> =
    CprSetting1::cpr1_4096;
// ...
} // ns enum_traits

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

Давайте добавим перегрузку к функции записи члена:

struct BitField : Tregister {
    // ...
    template <typename T, T... Cs>
    void write(std::integer_sequence<T, Cs...> s) {
        constexpr auto v_ = enum_traits::ENUM_VALUE<Type, decltype(s)>;
        static_assert(
            !std::is_pointer_v<decltype(v_)>,
            "Invalid enum int sequence provided");
        write(v_);
    }
};

В конце код клиента будетвыглядеть так:

field_int.write("cpr1_4096"_as_iseq);

Теперь мы говорим!Демо на Coliru .

...