Определение типа шаблона в более поздний момент времени C ++ - PullRequest
0 голосов
/ 14 декабря 2018

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

Я думаю, что будет проще показать, что я пытаюсь сделать, чем описать это.

helper.h

#include <iostream>
#include <type_traits>
#include <utility>
#include <string>
#include <sstream>
#include <stdexcept>
using namespace std;
template <typename T>
class helper
{
public:
    helper()
    {
      stringstream temp;
      if(is_same<T, short>::value)
      {
        temp << 1;
      }
      else if(is_same<T,long>::value)
      {
        temp << 1024;
      }
      else if(is_same<T, char*>::value)
      {
        temp << "Hello";
      }
      else if(is_same<T, string>::value)
      {
        temp << "Hello World";
      }
      else
      {
        throw invalid_argument("Error in helper: Unknown data type" + to_string(__LINE__) +  string(__FILE__));
      }

      temp >> data;
    }


    T getData()
    {
      return data;
    }

  protected:
    T data;
};

call.cpp

#include <iostream>
#include "helper.h"
using namespace std;

int main()
{
  helper<> my_helper;

  int data;
  cin >> data;
  switch(data)
  {
    case 1:
      my_helper = helper<short>;
      break;
    case 2:
      my_helper = helper<long>;
      break;
    case 3:
      my_helper = helper<char *>;
      break;
    default:
      my_helper = helper<string>;
      break;
  }
  cout << my_helper.getData() << endl;
  return 0;
}

Теперь, это не скомпилируется, потому что у помощника нет аргумента шаблона, но естькаким образом я могу установить аргумент в более поздний момент времени (например, после ввода данных пользователем, как показано в примере)? После того, как аргумент установлен, не будет прецедента, когда он изменится. Я знаю, что это тривиальный пример, где я мог бы просто сделать cout в switch-case, но это концепция, которую я хочу реализовать.

К сожалению, я застрял с C ++ 11 и не использую Boost Libs, в противном случае я думаю, что мог бы использовать std::any, я думал, что мог бы использовать void указатели, но тогда мне нужно будет указать тип данных, когда я вызываю reinterpret_cast.

Если есть какая-либо дополнительная информация, которую я могу предоставить, или что-либо, что я могу уточнить, пожалуйста, дайте мне знать!

Ответы [ 2 ]

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

Ответ на ваш вопрос зависит от целей, которые вы пытаетесь достичь.Итак, первое.Не забывайте, что шаблоны и ООП, как правило, ортогональны.Каждая специализация шаблона - это новый тип, который не связан с другими.Поэтому единственный способ привести все специализации к одному и тому же типу - это наследовать от одного базового класса.Итак

class AbstractHelper
{
public:
   virtual ~AbstractHelper() {}
};

template <typename T>
class helper : public AbstractHelper

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

class AbstractHelper
{
public:
   virtual string getDataString() { return ""; }
   ...

template <typename T>
class helper : public AbstractHelper
{
public:
string getDataString()
   {
      return to_string(data);
   }

Если это не приемлемо, вам нужно ответить на вопрос, возможно ли полностью инкапсулировать обработку данных?Предположим, вы можете.Затем

class AbstractHelper
{
public:
   virtual void printData() {  }
   ...

template <typename T>
class helper : public AbstractHelper
{
public:
void printData()
   {
      cout << data << endl;;
   }

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

enum Type
{
   TYPE_SHORT,
   TYPE_LONG,
   TYPE_STRING
...

class AbstractHelper
{
protected:
   Type type_;
public:
   Type getType() { return type_; }
......

template <typename T>
class helper : public AbstractHelper
{
public:
   helper()
   {
      stringstream temp;
      if (is_same<T, short>::value)
      {
         temp << 1;
         type_ = TYPE_SHORT;
      }
      else if (is_same<T, long>::value)
      {
         temp << 1024;
         type_ = TYPE_LONG;
      }
      ...

и выбрать нужный обработчик:

 switch (my_helper.getType())
   {
   case TYPE_SHORT:
      cout << dynamic_cast<helper<short>&>(my_helper).getData() << endl;
      break;
   case TYPE_LONG:
      cout << dynamic_cast<helper<long>&>(my_helper).getData() << endl;

Не смущайтесь количеством ручного кода - макросыи шаблоны могут значительно уменьшить его.

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

helper<short> - это один тип, а helper<char *> - другой, полностью несовместимый тип.Нет типа helper<>, который может быть назначен обоими.Для этого вы можете использовать базовый класс:

class base {
   virtual ~base() = default;
};

template <typename T>
class helper: public base
{
    // your code
};


int main()
{
  std::unique_ptr<base> my_helper;

  int data;
  cin >> data;
  switch(data)
  {
    case 1:
      my_helper.reset(new helper<short>);
      break;
    case 2:
      my_helper.reset(new helper<long>);
      break;
    case 3:
      my_helper.reset(new helper<char *>);
      break;
    default:
      my_helper.reset(new helper<string>);
      break;
  }
  //cout << my_helper->getData() << endl;
  return 0;
}

Но тогда я не думаю, что есть способ объявить virtual T getData() внутри base.

...