различные конструкторы подклассов variadic arg - PullRequest
0 голосов
/ 26 мая 2018

У меня есть следующие структуры:

struct s0 {
    char name[64];
    s0* parent;
    int stackLevel;

    s0(s0* parent_, char* nameMask_, ...){
        va_list args;
        va_start(args, nameMask_);
        vsprintf_s(name, 64, nameMask_, args);
        va_end(args);
        parent=parent_;
    }

    ~s0(){}
};

struct s1 : s0 {
   int p1;

   s1(s0* parent_, int p1_, char* nameMask_, ...) : s0(parent_, nameMask_) {
        p1=p1_;
   }
}

Я ищу способ, чтобы конструктор s1 передавал свои переменные аргументы конструктору s0.Я начал изучать пакеты параметров и различные шаблоны, но я просто не понимаю, как это будет работать в этом контексте.Возможно ли вообще то, что я ищу?

Ответы [ 2 ]

0 голосов
/ 27 мая 2018

Как вы предлагаете, можно использовать шаблоны с переменным числом, если вы можете использовать C ++ 11.Новый конструктор s0 будет выглядеть примерно так:

template<typename ... Args>
s0(s0* parent_, const char* nameMask_, Args... args) : parent(parent_) {
  sprintf(name, nameMask_, args...);
}

Эта конструкция также может быть использована для s1, чтобы напрямую вызывать конструктор s0.

Однако я думаю, что есть две проблемыпринять во внимание:

  • Теперь, когда конструктор шаблонизирован, компилятор создает новый конструктор для каждого списка параметров.Время компиляции увеличится.
  • Теперь тип параметров известен внутри конструктора.Однако код не использует эти данные, потому что он вызывает sprintf, который использует переменные аргументы.Может быть интересно использовать другую библиотеку форматирования.

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

#include <iostream>
#include <cstdio>

struct s0 {
  char name[64];
  s0* parent; 
  int stackLevel;

  template<typename ... Args>
  s0(s0* parent_, const char* nameMask_, Args... args)
    : parent(parent_) {
    sprintf(name, nameMask_, args...);
  }
};

struct s1 : s0 {
  int p1;

  template<typename ... Args>
  s1(s0* parent_, int p1_, const char* nameMask_, Args... args)
    : s0(parent_, nameMask_, args...), p1(p1_) {
  }
};
0 голосов
/ 26 мая 2018

Несколько замечаний до ...

  1. Переменные аргументы добавляют что-то важное для C. (Без них семейство printf() вряд ли можно было бы реализовать в чистом C.)основан на макросах, которые сокращают разрыв между уникальным кодом и различными реализациями, которые очень зависят от платформы и оборудования.В C ++ использование таких макросов должно быть максимально предотвращено в целом.template обеспечивают безопасную альтернативу типа.

  2. Учитывая, что заменой C ++ для семейства printf() являются потоковые классы с множеством перегруженных операторов сдвига, IMHO, в определенныхВ ситуациях принцип со строкой форматирования и переменным числом аргументов является гораздо более подходящим (например, для вывода уведомлений или сообщений журнала во время выполнения, где дополнительно должна применяться локализация, которая может даже изменить порядок вывода форматированных аргументов).Потоковые классы с операторами сдвига не могут быть использованы для этого.Несколько лет назад, когда мы использовали gtkmm, я нашел Glib::ustring::compose() очень подходящим для этой работы.Тем не менее, он (как и printf()) не "безопасен во время компиляции", это по крайней мере безопасный тип, и проверки во время выполнения могут быть легко применены.И: AFAIK, он не содержит макросов - реализован только в чистом коде C ++.

Однако, имея в виду приведенный выше отказ от ответственности, я попытался понять / решить проблему OP, касающуюся реализации с помощьюпеременные аргументы.При написании примера кода я просто понял, почему становится проблемой вызвать конструктор, принимающий va_list из другого конструктора.Следуя схеме, которая хорошо представлена ​​в ответе SO: Передача аргументов переменной в другую функцию, которая принимает список аргументов переменной , создает следующий код:

struct s0 {
  s0(const char *fmt, va_list args);
};

struct s1: s0 {
  s1(const char *fmt, ...):
    va_list args;
    va_start(args, fmt);
    s0(fmt, args)
    va_end(args);
  { }
};

Ой, неправильно !Я действительно хотел бы знать, есть ли какая-либо комбинация платформы / оборудования, где это успешно скомпилируется.Итак, вызов конструктора базового класса должен быть перенесен в тело:

struct s1: s0 {
  s1(const char *fmt, ...)
  {
    va_list args;
    va_start(args, fmt);
    s0(fmt, args);
    va_end(args);
  }
};

Ой, неправильно снова!Помимо того, что я даже не знаю, может ли конструктор вызываться в теле функции, я не могу предотвратить неявный вызов конструктора по умолчанию в этом случае, который недоступен.

Итак, яВ итоге мы убедились, что конструктор с va_list является , а не решением в этом случае.Я не вижу другого способа отделить функцию, которая получает / обрабатывает va_list:

#include <iostream>
#include <cstdio>
#include <cstdarg>

struct s0 {
    char name[64];
    s0 *parent;
    int stackLevel;

    s0(s0 *parent, const char *nameMask, ...):
      parent(parent)
    {
      va_list args;
      va_start(args, nameMask);
      vsnprintf(name, sizeof name, nameMask, args);
      va_end(args);
    }

    ~s0() = default;

  protected:
    // constructor for derived classes
    s0(s0 *parent): name(""), parent(parent) { }

    // construction of name
    void initName(const char *nameMask, va_list args)
    {
      vsnprintf(name, sizeof name, nameMask, args);
    }
};

struct s1: s0 {
    int p1;

    s1(s0 *parent, int p1, const char *nameMask, ...):
      s0(parent), p1(p1)
    {
      va_list args;
      va_start(args, nameMask);
      initName(nameMask, args);
      va_end(args);
    }

    ~s1() = default;
};

int main(int argc, char *argv[])
{
  s0 s0(nullptr, "s0[%d]", 0);
  std::cout << "s0 '" << s0.name << "'\n";
  s1 s1(&s0, 1, "s1[%d]", 1);
  std::cout << "s1 '" << s1.name << "'\n";
  return 0;
}

Вывод:

s0 's0[0]'
s1 's1[1]'

Демонстрация в реальном времени на coliru

Примечание:

При реализации примера я понял, что vsprintf_s() является частью стандарта C11, ноне C ++ 11.Поэтому я заменил его на std::vsnprintf(), который работает достаточно похоже.

...