Подтвердить что ты не робот

Как я могу перебирать список упакованных вариационных шаблонов?

Я пытаюсь найти метод для итерации над списком аргументов шаблона шаблона пакета. Теперь, как и при всех итерациях, вам нужен какой-то метод определения количества аргументов в упакованном списке и, что более важно, как индивидуально получать данные из списка упакованных аргументов.

Общая идея состоит в том, чтобы перебирать список, хранить все данные типа int в векторе, хранить все данные типа char * в векторе и хранить все данные типа float в вектор. Во время этого процесса также должен быть отдельный вектор, в котором хранятся отдельные символы того порядка, в который вошли аргументы. Например, когда вы push_back (a_float), вы также делаете push_back ('f'), который просто хранится индивидуальный char, чтобы знать порядок данных. Я мог бы также использовать std::string здесь и просто использовать + =. В качестве примера использовался только вектор.

Теперь, когда объект разработан, сама функция строится с использованием макроса, несмотря на злые намерения, требуемые, поскольку это эксперимент. Таким образом, буквально невозможно использовать рекурсивный вызов, поскольку фактическая реализация, в которой будет размещаться все это, будет расширяться во время компиляции; и вы не можете отменить макрос.

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

foo(arg(1), arg(2.0f), arg("three");

Итак, реальный вопрос: как я могу обойтись без такого? Чтобы вы, ребята, лучше понимали, что на самом деле делает код, я применил оптимистичный подход, который я использую в настоящее время.

struct any {
  void do_i(int   e) { INT    = e; }
  void do_f(float e) { FLOAT  = e; }
  void do_s(char* e) { STRING = e; }

  int   INT;
  float FLOAT;
  char *STRING;
};


template<typename T> struct get        { T      operator()(const any& t) { return T();      } };
template<>           struct get<int>   { int    operator()(const any& t) { return t.INT;    } };
template<>           struct get<float> { float  operator()(const any& t) { return t.FLOAT;  } };
template<>           struct get<char*> { char*  operator()(const any& t) { return t.STRING; } };

#define def(name)                                  \
  template<typename... T>                          \
  auto name (T... argv) -> any {                   \
   std::initializer_list<any> argin = { argv... }; \
    std::vector<any> args = argin;
#define get(name,T)  get<T>()(args[name])
#define end }

any arg(int   a) { any arg; arg.INT    = a; return arg; }
any arg(float f) { any arg; arg.FLOAT  = f; return arg; }
any arg(char* s) { any arg; arg.STRING = s; return arg; }

Я знаю, что это противно, однако это чистый эксперимент и не будет использоваться в производственном коде. Это чисто идея. Вероятно, это можно было бы сделать лучше. Но пример того, как вы будете использовать эту систему:

def(foo)
  int data = get(0, int);
  std::cout << data << std::endl;
end

очень похож на python. он тоже работает, но единственная проблема заключается в том, как вы вызываете эту функцию. Вот краткий пример:

foo(arg(1000));

Мне нужно создать новый любой тип, который очень эстетичен, но не означает, что эти макросы тоже не являются. Кроме того, я просто хочу, чтобы сделать:   Foo (1000);

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

Также следует отметить, что я хорошо знаю, что это не совсем дружественный тип, поскольку я поддерживаю только int, float, char * и все в порядке со мной. Мне больше ничего не нужно, и я добавлю проверки на использование type_traits для проверки того, что переданные аргументы действительно являются правильными для создания ошибки времени компиляции, если данные неверны. Это не проблема. Мне также не нужна поддержка для чего-то другого, кроме этих типов POD.

Было бы очень приятно, если бы я мог получить конструктивную помощь, против аргументов в отношении моего чисто нелогичного и глупого использования макросов и POD только типов. Я хорошо знаю, насколько хрупким и сломанным является код. Это эксперимент, и я могу позже исправить проблемы с не-POD-данными и сделать его более безопасным и безопасным для использования.

Спасибо за ваше внимание, и я с нетерпением жду, чтобы помочь.

4b9b3361

Ответ 1

Если вы хотите связать аргументы с any, вы можете использовать следующую настройку. Я также сделал класс any более удобным для использования, хотя он не является технически классом any.

#include <vector>
#include <iostream>

struct any {
  enum type {Int, Float, String};
  any(int   e) { m_data.INT    = e; m_type = Int;}
  any(float e) { m_data.FLOAT  = e; m_type = Float;}
  any(char* e) { m_data.STRING = e; m_type = String;}
  type get_type() const { return m_type; }
  int get_int() const { return m_data.INT; }
  float get_float() const { return m_data.FLOAT; }
  char* get_string() const { return m_data.STRING; }
private:
  type m_type;
  union {
    int   INT;
    float FLOAT;
    char *STRING;
  } m_data;
};

template <class ...Args>
void foo_imp(const Args&... args)
{
    std::vector<any> vec = {args...};
    for (unsigned i = 0; i < vec.size(); ++i) {
        switch (vec[i].get_type()) {
            case any::Int: std::cout << vec[i].get_int() << '\n'; break;
            case any::Float: std::cout << vec[i].get_float() << '\n'; break;
            case any::String: std::cout << vec[i].get_string() << '\n'; break;
        }
    }
}

template <class ...Args>
void foo(Args... args)
{
    foo_imp(any(args)...);  //pass each arg to any constructor, and call foo_imp with resulting any objects
}

int main()
{
    char s[] = "Hello";
    foo(1, 3.4f, s);
}

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

Ответ 2

Это не то, как обычно можно использовать шаблоны Variadic, а не вообще.

Итерации по вариационному набору невозможны в соответствии с языковыми правилами, поэтому вам нужно обратиться к рекурсии.

class Stock
{
public:
  bool isInt(size_t i) { return _indexes.at(i).first == Int; }
  int getInt(size_t i) { assert(isInt(i)); return _ints.at(_indexes.at(i).second); }

  // push (a)
  template <typename... Args>
  void push(int i, Args... args) {
    _indexes.push_back(std::make_pair(Int, _ints.size()));
    _ints.push_back(i);
    this->push(args...);
  }

  // push (b)
  template <typename... Args>
  void push(float f, Args... args) {
    _indexes.push_back(std::make_pair(Float, _floats.size()));
    _floats.push_back(f);
    this->push(args...);
  }

private:
  // push (c)
  void push() {}

  enum Type { Int, Float; };
  typedef size_t Index;

  std::vector<std::pair<Type,Index>> _indexes;
  std::vector<int> _ints;
  std::vector<float> _floats;
};

Пример (в действии), предположим, что Stock stock;:

  • stock.push(1, 3.2f, 4, 5, 4.2f); разрешен (a), поскольку первым аргументом является int
  • this->push(args...) расширяется до this->push(3.2f, 4, 5, 4.2f);, который разрешен (b), поскольку первым аргументом является float
  • this->push(args...) расширяется до this->push(4, 5, 4.2f);, который разрешен (a), поскольку первым аргументом является int
  • this->push(args...) расширяется до this->push(5, 4.2f);, который разрешен (a), поскольку первым аргументом является int
  • this->push(args...) расширяется до this->push(4.2f);, который разрешен (b), поскольку первым аргументом является float
  • this->push(args...) расширяется до this->push();, который разрешен в (c), поскольку нет аргумента, таким образом заканчивая рекурсию

Таким образом:

  • Добавление другого типа для обработки так же просто, как добавление другой перегрузки, изменение первого типа (например, std::string const&)
  • Если передан совершенно другой тип (например, Foo), то перегрузка не может быть выбрана, что приведет к ошибке времени компиляции.

Одно предупреждение: автоматическое преобразование означает, что double будет выбирать перегрузку (b), а short будет выбирать перегрузку (a). Если это нежелательно, необходимо ввести SFINAE, что делает метод немного сложнее (ну, по крайней мере, их подписи), например:

template <typename T, typename... Args>
typename std::enable_if<is_int<T>::value>::type push(T i, Args... args);

Где is_int будет что-то вроде:

template <typename T> struct is_int { static bool constexpr value = false; };
template <> struct is_int<int> { static bool constexpr value = true; };

Другой альтернативой, однако, было бы рассмотреть вариант типа. Например:

typedef boost::variant<int, float, std::string> Variant;

Он уже существует со всеми утилитами, он может быть сохранен в vector, скопирован и т.д.... и, похоже, очень похож на то, что вам нужно, даже если он не использует шаблоны Variadic.

Ответ 3

Вы можете создать его контейнер, инициализируя его пакетом параметров между {}. Пока тип параметров... является однородным или, по крайней мере, преобразуется в тип элемента вашего контейнера, он будет работать. (проверено с помощью g++ 4.6.1)

#include <array>

template <class... Params>
void f(Params... params) {
    std::array<int, sizeof...(params)> list = {params...};
}

Ответ 5

В настоящее время нет конкретной функции, но вы можете использовать некоторые обходные пути.

Использование списка инициализации

В одном обходном решении используется тот факт, что подвыражения списков инициализации оцениваются по порядку. int a[] = {get1(), get2()} выполнит get1 перед выполнением get2. Может быть, fold выражения пригодится для подобных методов в будущем. Чтобы вызвать do() для каждого аргумента, вы можете сделать что-то вроде этого:

template <class... Args>
void doSomething(Args... args) {
    int x[] = {args.do()...};
}

Однако это будет работать только тогда, когда do() возвращает int. Вы можете использовать оператор запятая для поддержки операций, которые не возвращают правильное значение.

template <class... Args>
void doSomething(Args... args) {
    int x[] = {(args.do(), 0)...};
}

Чтобы сделать более сложные вещи, вы можете поместить их в другую функцию:

template <class Arg>
void process(Arg arg, int &someOtherData) {
    // You can do something with arg here.
}

template <class... Args>
void doSomething(Args... args) {
    int someOtherData;
    int x[] = {(process(args, someOtherData), 0)...};
}

Обратите внимание, что с общим lambdas (С++ 14) вы можете определить функцию для этого шаблона для вас.

template <class F, class... Args>
void do_for(F f, Args... args) {
    int x[] = {(f(args), 0)...};
}

template <class... Args>
void doSomething(Args... args) {
    do_for([&](auto arg) {
        // You can do something with arg here.
    }, args...);
}

Использование рекурсии

Другая возможность - использовать рекурсию. Вот небольшой пример, который определяет аналогичную функцию do_for, как указано выше.

template <class F, class First, class... Rest>
void do_for(F f, First first, Rest... rest) {
    f(first);
    do_for(f, rest...);
}
template <class F>
void do_for(F f) {
    // Parameter pack is empty.
}

template <class... Args>
void doSomething(Args... args) {
    do_for([&](auto arg) {
        // You can do something with arg here.
    }, args...);
}

Ответ 6

Вы можете использовать несколько вариативных шаблонов, это немного грязно, но оно работает и легко понять. У вас просто есть функция с вариационным шаблоном:

template <typename ...ArgsType >
void function(ArgsType... Args){
     helperFunction(Args...);
}

И вспомогательная функция, например:

void helperFunction() {}

template <typename T, typename ...ArgsType >
void helperFunction(T t, ArgsType... Args) {

     //do what you want with t
    function(Args...);

}

Теперь, когда вы вызываете "функцию", "helperFunction" будет вызываться и изолировать первый переданный параметр от остального, эта переменная может использоваться для вызова другой функции (или чего-то еще). Затем "функция" будет вызываться снова и снова, пока осталось больше переменных. Обратите внимание, что вам может потребоваться объявить helperClass перед "функцией".

Окончательный код будет выглядеть следующим образом:

void helperFunction();

template <typename T, typename ...ArgsType >
void helperFunction(T t, ArgsType... Args);

template <typename ...ArgsType >
void function(ArgsType... Args){
     helperFunction(Args...);
}

void helperFunction() {}

template <typename T, typename ...ArgsType >
void helperFunction(T t, ArgsType... Args) {

     //do what you want with t
    function(Args...);

}

Код не проверен.