Разрешить другую стратегию во время компиляции по размеру параметра "T" - PullRequest
0 голосов
/ 08 декабря 2018

Я пытаюсь внедрить наивную систему GC для целей обучения.По сути, он предоставит пользователям интерфейс newElement для «нового» объекта.

Стратегия, которую я хочу решить, состоит в том, чтобы выделить объект по другой стратегии (пул для небольшого объекта / malloc длябольшой ...) в соответствии с типом объекта, например:

T* newElement(Args&&... args)
{
    if(sizeof(T) < 4){
        // strategy1
        return newImpl1(std::forward<Args>(args)...);
    } else if(4 <= sizeof(T) < 16){
        // strategy2
        return newImpl2(std::forward<Args>(args)...);
    } else{
        // strategy3
        return newImpl3(std::forward<Args>(args)...);
    }
}

я думаю, что эта стоимость может быть во время компиляции, но не во время выполнения, так как sizeof(T) может быть оценена во время компиляции.Я знаю, что в C ++ 17 у нас есть такие функции, как constexpr if для решения подобных ситуаций.Тем не менее, я имею дело с VS 2015, он поддерживает только C ++ 11 и C ++ 14.Итак, я рассматриваю процесс как две разные фазы:

  1. «новый» должен принимать разные виды меток (по типу) для разрешения другой стратегии

  2. «диспетчеризация» должна принимать T в качестве входных данных и иметь способ для вывода правильной метки (по типу)

Как правило, целью фазы 2 является вывод различных типовметки (независимо от значений или типов) с помощью ряда условных выражений.

На ум приходят два вида решений.

enum class Strategy {
    small, middle, big
};

constexpr size_t SmallMiddleThreshold = 4;
constexpr size_t MiddleBigThreshold = 8;

template
<Strategy s=Strategy::small>
struct newImpl {
    template
    <typename T, typename... Args>
    static T* apply(Args&&... args)
    {
        cout << "small!" << endl;
        return new T(std::forward<Args>(args)...);
    }
};

template
<>
struct newImpl<Strategy::middle> {
    template
    <typename T, typename... Args>
    static T* apply(Args&&... args)
    {
        cout << "middle!" << endl;
        return new T(std::forward<Args>(args)...);
    }
};

template
<>
struct newImpl<Strategy::big> {
    template
    <typename T, typename... Args>
    static T* apply(Args&&... args)
    {
        cout << "big!" << endl;
        return new T(std::forward<Args>(args)...);
    }
};

Решение 1

используйте шаблоны переменных для расширения условных выражений.

template
<bool Condition1=true, bool... Conditions>
struct SizeDispatcher1 {
    constexpr static Strategy value = Strategy::small;
};

template
<bool... Conditions>
struct SizeDispatcher1<false, Conditions...> {
    constexpr static Strategy value = SizeDispatcher2<Conditions...>::value;
};

template
<bool Condition2 = true, bool... Conditions>
struct SizeDispatcher2 {
    constexpr static Strategy value = Strategy::middle;
};

template
<bool... Conditions>
struct SizeDispatcher2<false, Conditions...> {
    constexpr static Strategy value = SizeDispatcher3<Conditions...>::value;
};

template
<bool Condition3 = true, bool... Conditions>
struct SizeDispatcher3 {
    constexpr static Strategy value = Strategy::big;
};

template
<typename T>
struct SizeDispatcher {
    constexpr static Strategy value = 
        SizeDispatcher1< 
            sizeof(T) < SmallMiddleThreshold,
            SmallMiddleThreshold <= sizeof(T) && sizeof(T) < MiddleBigThreshold,
            MiddleBigThreshold <= sizeof(T)
        >::value;
};

template
<typename T, typename... Args>
T* newElement(Args&&... args)
{
    return newImpl<SizeDispatcher<T>::value>::apply<T>(std::forward<Args>(args)...);
}

Решение 2

используйте частичную специализацию для сопоставления различных случаев.

template
<bool Condition1=true, bool Condition2=false, bool Condition3=false>
struct SizeDispatcherImpl {
    constexpr static Strategy value = Strategy::small;
};

template
<>
struct SizeDispatcherImpl<false, true, false> {
    constexpr static Strategy value = Strategy::middle;
};

template
<>
struct SizeDispatcherImpl<false, false, true> {
    constexpr static Strategy value = Strategy::big;
};

template
<typename T>
struct SizeDispatcher {
    constexpr static Strategy value = 
        SizeDispatcherImpl< 
            sizeof(T) < SmallMiddleThreshold,
            SmallMiddleThreshold <= sizeof(T) && sizeof(T) < MiddleBigThreshold,
            MiddleBigThreshold <= sizeof(T)
        >::value;
};

Однако яУ меня есть вопросы по поводу приведенного выше кода.

Во-первых, может ли он правильно выполнить мое требование?То есть, чтобы разрешить разные стратегии во время компиляции?

Во-вторых, оба решения имеют как минимум следующие недостатки: 1. Диспетчер тесно связан с условными выражениями (формат, последовательность ...), что определенно не является хорошей практикой кодирования.2. не имеют четкой семантики.

Итак, как правильно и лучше решить вопрос, если это возможно? (Для создания другой метки с помощью серии условных выражений)

1 Ответ

0 голосов
/ 08 декабря 2018

Эти решения кажутся немного сложными.Другим решением может быть использование перегруженных вспомогательных функций или, возможно, даже перегрузка newElement с ограничениями SFINAE.Одним из преимуществ является то, что условие можно увидеть прямо рядом с реализацией.

 #include <type_traits>
 #include <iostream>

 template <typename T, typename... Args, std::enable_if_t<(sizeof(T) < 4)>* = nullptr>
 T* newElement(Args&& ... args) {
     std::cout << "small!" << std::endl;
     return new T(std::forward<Args>(args)...);
 }

 template <typename T, typename... Args,
     std::enable_if_t<(sizeof(T) >= 4 && sizeof(T) < 16)>* = nullptr>
 T* newElement(Args&& ... args) {
     std::cout << "middle!" << std::endl;
     return new T(std::forward<Args>(args)...);
 }

 template <typename T, typename... Args, std::enable_if_t<(sizeof(T) >= 16)>* = nullptr>
 T* newElement(Args&& ... args) {
     std::cout << "big!" << std::endl;
     return new T(std::forward<Args>(args)...);
 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...