Интерфейс класса, который может создавать N методов

Я работаю над созданием динамической формы на 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::vector из entries.

NathanOliver 08.10.2018 19:17

извините, но мне непонятно, чего вы хотите; вы можете написать пример псевдокода?

max66 08.10.2018 19:18

Добавлен пример псевдокода для пояснения

Programmer 08.10.2018 19:24
pastebin.com/fErrk7zQ Я не знаю, как заставить его генерировать функции для вас и чтобы ваш подкласс автоматически реализовывал их .. Это может быть невозможно afaik: S
Brandon 08.10.2018 19:49

Как насчет virtual Field(int field_index, char * name , int value) =0;?

user7860670 08.10.2018 20:08

Я считаю, что он создаст только один метод интерфейса - так как мы можем заставить пользователя ввести такое количество записей?

Programmer 08.10.2018 20:11

Проверить количество созданных записей ...

user7860670 08.10.2018 20:29

Тогда проблема, которую я чувствую, заключается в том, что нет методов для ввода N записей?

Programmer 08.10.2018 20:31

Ничего не мешает ввести N записей ...

user7860670 08.10.2018 20:32

Но это интерфейс, для которого будет создан класс реализации - следовательно, когда объект класса создается, пользователю необходимо переопределить методы, которые являются фактической логикой для реализации.

Programmer 08.10.2018 20:54
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
10
83
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Есть много разных способов сделать это.

Вы сказали, что хотите, чтобы все поля были одного типа.

Вот простое решение с вектором.

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

/*********************
 *  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;
}

Это все.

Спасибо - этого достаточно, но есть ли способ заставить код выдавать ошибку компиляции, пока не будут установлены все поля?

Programmer 09.10.2018 04:45

Не уверен, что понимаете, что именно вы хотите, но ... Я полагаю, вы можете использовать рекурсию и тип тега (скажем, std::integral_constant<std::size_t, N>, где N - это аргумент шаблона для Field), чтобы различать геттер и сеттер на разных уровнях.

Я имею в виду ... учитывая следующий 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<0>, которая определяет поддельные методы

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 виртуальных сеттеров и 20 (10 для имен и 10 для значений) виртуальных получателей.

Ниже приведен полный рабочий пример.

#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;

Спасибо за пример - этого достаточно, но есть ли способ заставить код выдавать ошибку компиляции, пока не будут установлены все поля?

Programmer 09.10.2018 04:46

Я предполагаю ошибку в методе шаблона get-field (см. Последнюю часть моего ответа); Например, вы можете добавить логический флаг в каждую специализацию Field и метод проверки флага в следующем Field (и, рекурсивно, каждый флаг).

max66 09.10.2018 10:48

Другие вопросы по теме