【问题标题】:Class Interface which can create N number of methods可以创建N个方法的类接口
【发布时间】:2018-10-08 17:15:25
【问题描述】:

我正在用 C++ 创建动态表单,它需要让用户输入 N 个条目。数字 N 取决于用户决定的应用程序类型 - 假设所有字段都属于同一类型。客户端将继承该接口。

因此我试图创建一个界面(使用模板或任何其他方法)但无法创建这样的界面。

是否可能 - 如果可以,请提供示例?

10 个字段的表单伪代码示例:

template<int i>
class Field {
public:
    Field () {  
    for (int index = 0 ; index < i ; index ++)

    }
};

template<>
class Field<1> {
public:
Field(char * name , int value);
};

class Form : public Field<10>
{
 virtual Field1 (char * name , int value) =0;
 ..........................................
 virtual Field10 (char * name , int value) =0;
 // so based upon the value of N provided this class should have N pure virtual methods
}

【问题讨论】:

  • 听起来你需要 std::vectorentries
  • 对不起,但不清楚,对我来说,你想要什么;你能写一个伪代码例子吗?
  • 为澄清添加了伪代码示例
  • pastebin.com/fErrk7zQ 我不知道如何让它为你生成函数并让你的子类自动实现它。这可能是不可能的 afaik :S
  • virtual Field(int field_index, char * name , int value) =0; 怎么样?

标签: c++ algorithm c++11


【解决方案1】:

有很多不同的方法可以做到这一点。

你说你想要所有相同类型的字段。

这是一个带有向量的简单解决方案。

如果您想按名称而不是按索引访问字段,也可以轻松地将我的示例更改为使用地图。

/*********************
 *  File: main.cpp   *
 *********************/

#include <iostream>
#include "Form.h"

using std::cout;
using std::endl;

int main(int argc, char* argv[])
{
    Form<int> form(15);

    // The first field is the customer number
    form.setFieldName(0, "customer number");
    form.setFieldValue(0, 4711);

    // The second field is the order number
    form.setFieldName(1, "order number");
    form.setFieldValue(1, 1234);

    // The third field is an article number
    form.setFieldName(2, "article number");
    form.setFieldValue(2, 6789);

    // ... and so on ...
    // Read some values back

    cout << "This is the second field:" << endl;
    cout << "Field Name:  [" << form.getFieldName(1) << "]" << endl;
    cout << "Field Value: [" << form.getFieldValue(1) << "]" << endl;

    return 0;
}

这是 Main.cpp 中包含的模板:

/*****************
 * File: Form.h  *
 *****************/

#pragma once
#include "FormField.h"

template<class T>
class Form
{
public:
    explicit Form(const int size);
    virtual ~Form() = default;
    void setFieldName(int index, const std::string& name);
    const std::string& getFieldName(int index) const;
    void setFieldValue(int index, const T& value);
    const T& getFieldValue(int index) const;
private:
    std::vector<FormField<T>> formFields;
};

template<class T>
Form<T>::Form(const int size)
    : formFields(size)
{ }

template<class T>
void Form<T>::setFieldName(int index, const std::string& name)
{
    formFields[index].setName(name);
}

template<class T>
const std::string& Form<T>::getFieldName(int index) const
{
    return formFields[index].getName();
}

template<class T>
void Form<T>::setFieldValue(int index, const T& value)
{
    formFields[index].setValue(value);
}

template<class T>
const T& Form<T>::getFieldValue(int index) const
{
    return formFields[index].getValue();
}

这是 FormField 的模板:

/**********************
 * File: FormField.h  *
 **********************/

#pragma once
#include <string>
#include <vector>

template<class T>
class FormField
{
public:
    explicit FormField();
    virtual ~FormField() = default;
    const std::string& getName() const;
    void setName(const std::string& name);
    const T& getValue() const;
    void setValue(const T& value);
private:
    std::string name;
    T value;
};

template<class T>
FormField<T>::FormField()
    : name(), value()
{
}

template<class T>
const std::string& FormField<T>::getName() const
{
    return name;
}

template<class T>
const T& FormField<T>::getValue() const
{
    return value;
}

template<class T>
void FormField<T>::setName(const std::string& name)
{
    this->name = name;
}

template<class T>
void FormField<T>::setValue(const T& value)
{
    this->value = value;
}

就是这样。

【讨论】:

  • 谢谢 - 它满足了需求,但有没有一种方法可以强制代码给出编译错误,直到所有字段都设置好?
【解决方案2】:

不确定你到底想要什么,但是......我想你可以使用递归和标签类型(比如std::integral_constant&lt;std::size_t, N&gt;,其中NField 的模板参数)来区分getter 和setter在不同的层次。

我的意思是...鉴于以下Field

template <typename T, std::size_t N>
class Field : public Field<T, N-1u>
 {
   private:
      std::string  name;
      T            value;

   public:
      using Field<T, N-1u>::setField;
      using Field<T, N-1u>::getFieldName;
      using Field<T, N-1u>::getFieldValue;

      using icnc = std::integral_constant<std::size_t, N> const;

      virtual void setField (icnc &, std::string const & n0, T const & v0)
       {
         name  = n0;
         value = v0;
       }

      virtual std::string const & getFieldName (icnc &) const
       { return name; }

      virtual T const & getFieldValue (icnc &) const
       { return value; }
 };

以及定义假方法的地面专业化Field&lt;0&gt;

template <typename T>
class Field<T, 0u>
 {
   public:
      // fake ground functions
      virtual void setField      () { };
      virtual void getFieldName  () { };
      virtual void getFieldValue () { };
 };

如下定义Form

class Form : public Field<int, 10u>
 { };

您有 10 个虚拟 setter 和 20 个(10 个用于名称,10 个用于值)虚拟 getter。

以下是一个完整的工作示例

#include <string>
#include <iostream>
#include <type_traits>

template <typename T, std::size_t N>
class Field : public Field<T, N-1u>
 {
   private:
      std::string  name;
      T            value;

   public:
      using Field<T, N-1u>::setField;
      using Field<T, N-1u>::getFieldName;
      using Field<T, N-1u>::getFieldValue;

      using icnc = std::integral_constant<std::size_t, N> const;

      virtual void setField (icnc &, std::string const & n0, T const & v0)
       {
         name  = n0;
         value = v0;
       }

      virtual std::string const & getFieldName (icnc &) const
       { return name; }

      virtual T const & getFieldValue (icnc &) const
       { return value; }
 };

template <typename T>
class Field<T, 0u>
 {
   public:
      // fake ground functions
      virtual void setField      () { };
      virtual void getFieldName  () { };
      virtual void getFieldValue () { };
 };

class Form : public Field<int, 3u>
 {
   // Form inherit three different setField, three different getFieldName
   // and three different getFieldValie methods
 };

template <std::size_t N>
using tag = std::integral_constant<std::size_t, N> const;


int main ()
 {
   Form  f;

   f.setField(tag<1u>{}, "field 1", 111);
   f.setField(tag<2u>{}, "field 2", 222);
   f.setField(tag<3u>{}, "field 3", 333);

   std::cout << f.getFieldName(tag<1u>{}) << ", "
      << f.getFieldValue(tag<1u>{}) << std::endl;
   std::cout << f.getFieldName(tag<2u>{}) << ", "
      << f.getFieldValue(tag<2u>{}) << std::endl;
   std::cout << f.getFieldName(tag<3u>{}) << ", "
      << f.getFieldValue(tag<3u>{}) << std::endl;
 }

Form 内部工作更多,添加一个模板设置器和几个模板获取器

class Form : public Field<int, 3u>
 {
   public:
      using Field<int, 3u>::setField;
      using Field<int, 3u>::getFieldName;
      using Field<int, 3u>::getFieldValue;

      template <std::size_t N>
      using tag = std::integral_constant<std::size_t, N> const;

      template <std::size_t N>
      void setField (std::string const & n0, int v0)
       { setField(tag<N>{}, n0, v0); }

      template <std::size_t N>
      std::string const & getFieldName () const
       { return getFieldName(tag<N>{}); }

      template <std::size_t N>
      int const & getFieldValue () const
       { return getFieldValue(tag<N>{}); }
 };

你可以让标签索引的使用更简单

   Form  f;

   f.setField<1u>("field 1", 111);
   f.setField<2u>("field 2", 222);
   f.setField<3u>("field 3", 333);

   std::cout << f.getFieldName<1u>() << ", " << f.getFieldValue<1u>()
      << std::endl;
   std::cout << f.getFieldName<2u>() << ", " << f.getFieldValue<2u>()
      << std::endl;
   std::cout << f.getFieldName<3u>() << ", " << f.getFieldValue<3u>()
      << std::endl;

【讨论】:

  • 感谢您提供的示例 - 它满足了需求,但有没有一种方法可以强制代码给出编译错误,直到所有字段都设置好?
  • 我想在 get-field 模板方法中有一个错误(见我回答的最后一部分);例如,您可以在每个 Field 特化中添加一个布尔标志,并在下面的 Field 中添加一个检查标志的方法(以及递归地,每个标志)。
猜你喜欢
  • 2016-02-26
  • 2021-01-03
  • 1970-01-01
  • 2021-02-02
  • 1970-01-01
  • 2013-08-14
  • 2011-03-11
  • 2010-11-05
  • 2011-10-13
相关资源
最近更新 更多