Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.80/5: Рейтинг темы: голосов - 5, средняя оценка - 4.80
5 / 5 / 0
Регистрация: 16.05.2012
Сообщений: 178
1

Побег из темницы типов (смесь шаблонов Pimpl и Double dispatch)

05.12.2018, 11:43. Показов 1032. Ответов 10
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Добрый день! Нашёл на хабре интересную статью про побег из темницы типов (смесь шаблонов Pimpl и Double dispatch), но пока не могу осуществить сей побег, так как пример, приведённый там, у меня даже не компилируется. Статья тут.
Пример, который я пытаюсь запустить такой:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <algorithm>
#include <iostream>
#include <memory>
#include <vector>
 
 
// Интерфейс класса доступен в общем API
class object
{
public:
    object();
    virtual ~object();
    virtual bool is_null() const;
    virtual std::string to_string() const;
protected:
    // Только объявление класса данных!
    class data;
private:
    std::shared_ptr<data> m_data;
};
 
// Реализация класса данных вне API
// должна быть недоступна для #include
class object::data
{
public:
    data() { }
    virtual ~data() { }
    virtual bool is_null() const { return true; }
    virtual std::string to_string() const { return "null"; }
};
 
// Интерфейс класса доступен в своем API
// не обязательно в той же библиотеке, что и object
class flower : public object
{
public:
    flower(std::string const& name);
    virtual bool is_null() const override;
    virtual std::string to_string() const override;
    virtual std::string name() const;
    virtual void rename(std::string const& name);
protected:
    // Только объявление класса данных!
    class data;
};
 
// Реализация класса данных вне API
// должна быть недоступна для #include
class flower::data : public object::data
{
public:
    static const std::string FLOWER_UNKNOWN;
    data()
        : m_name(FLOWER_UNKNOWN) {
    }
    data(std::string const& name)
        : m_name(name) {
    }
    virtual bool is_null() const override { return false; }
    virtual std::string to_string() const override {
        return "flower: " + m_name;
    }
    virtual std::string name() const { return m_name; }
    virtual void rename(std::string const& name) { m_name = name; }
private:
    std::string m_name;
};
 
 
int main(int argc, char *argv[])
{
  object rose = flower("rose");
  object none;
  std::vector<object> garden;
  garden.push_back(std::move(rose));
  garden.push_back(std::move(none));
  garden[1] = flower("gladiolus");
  std::for_each(garden.begin(), garden.end(),
      [](object const& element) {
         std::cout << element.to_string() << std::endl;
      }
  );
  return 0;
}

Для компиляции использую Qt Creator с настройками:

TEMPLATE = app
CONFIG += console c++17
CONFIG -= app_bundle
CONFIG -= qt

SOURCES += \
main.cpp

Компилятор - MinGW 32bit

Ошибки следующие:
Миниатюры
Побег из темницы типов (смесь шаблонов Pimpl и Double dispatch)  
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
05.12.2018, 11:43
Ответы с готовыми решениями:

Перевод типов в double
Здравствуйте! Потскажите пожалуйста, можно ли в кусочке моей программы сделать так, что бы...

Преобразование типов: строки в double
//Дана строка, состоящая из букв, цифр, запятых, точек, знаков ”+” и ”-”. //Вывести подстроку,...

Преобразование типов String->Double
Есть задача для калькулятора, сосчитать к примеру вот: 10.34+4.9 хочу преобразовать из String...

Деление типов float double
#include &quot;pch.h&quot; #include &lt;iostream&gt; #include &quot;math.h&quot; int main() { enum Names {Maxim,...

10
18902 / 9860 / 2410
Регистрация: 30.01.2014
Сообщений: 17,306
05.12.2018, 11:52 2
Цитата Сообщение от Юрий Ч Посмотреть сообщение
так как пример, приведённый там, у меня даже не компилируется
Ваш пример компилируется, но не линкуется из-за отсутствия определений некоторых функций (линковщик вам их все обозначил). Добавьте их и все будет нормально.
Очевидно, что статья не ставила целью дать рабочий исходный код, а лишь демонстрировала идею. Т.е. читатель должен был уловить суть и повторить такое у себя самостоятельно, а не списывать пример из статьи. Ну это в идеале.
0
5 / 5 / 0
Регистрация: 16.05.2012
Сообщений: 178
05.12.2018, 13:21  [ТС] 3
DrOffset, Спасибо! Запустить удалось с тривиальным наполнением методов класса object. Но теперь не совсем понятно, как проксировать методы класса data через методы object. В текущем примере так не работает:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class object
{
public:
    object() {}
    virtual ~object() {}
    virtual bool is_null() const { m_data->is_null(); } // тут ошибка
    virtual std::string to_string() const { return "null"; }
protected:
    // Только объявление класса данных!
    class data;
private:
    std::shared_ptr<data> m_data;
};
 
 
class object::data
{
public:
    data() { }
    virtual ~data() { }
    virtual bool is_null() const { return true; }
    virtual std::string to_string() const { return "null"; }
};
Разнести классы object и data по разным файлам? Но там, похоже, будет перекрёстные ссылки на хидера.
0
18902 / 9860 / 2410
Регистрация: 30.01.2014
Сообщений: 17,306
05.12.2018, 13:27 4
Цитата Сообщение от Юрий Ч Посмотреть сообщение
Но теперь не совсем понятно, как проксировать методы класса data через методы object.
Реализация должна быть в cpp. В том месте, где уже доступно определение object::data.
0
5 / 5 / 0
Регистрация: 16.05.2012
Сообщений: 178
05.12.2018, 16:42  [ТС] 5
Разнести классы по файлам удалось, но теперь непонятно, как реализовать методы класса flower. Член object::m_data то закрытый. Не подскажите как быть?

C++
1
2
3
4
5
6
7
8
9
10
11
12
class flower : public object
{
public:
    flower(std::string const& name);                            // Непонятно, как реализовать
    virtual bool is_null() const override;                        // Непонятно, как реализовать
    virtual std::string to_string() const override;           // Непонятно, как реализовать
    virtual std::string name() const;                            // Непонятно, как реализовать
    virtual void rename(std::string const& name);        // Непонятно, как реализовать
protected:
    // Только объявление класса данных!
    class data;
};
0
5 / 5 / 0
Регистрация: 16.05.2012
Сообщений: 178
06.12.2018, 17:09  [ТС] 6
Добрый вечер! Вроде всё собралось, заработало. Сделал по примерам из статьи и при помощи ваших советов. Спасибо! Единственный нюанс, привести значение объекта типа object к значению нативного типа при помощи конструкции
"template <typename value_type> value_type object::to() const;". Вот в этом загвоздка. Может знает как одолеть? Об таком методе речь идёт в комментариях к статье. Спасибо!
0
18902 / 9860 / 2410
Регистрация: 30.01.2014
Сообщений: 17,306
06.12.2018, 19:11 7
Юрий Ч, один из вариантов описан здесь (методы get и restore)

Вообще код в статье довольно спорный. Я бы вам посоветовал почитать еще что-то на эту тему, чтобы не получать однобокие знания.
0
5 / 5 / 0
Регистрация: 16.05.2012
Сообщений: 178
06.12.2018, 19:44  [ТС] 8
DrOffset, Мне очень нужно довести этот код до логической точки (конечно промежуточной), поэтому буду очень благодарен в помощи реализации:

C++
1
template <typename value_type> value_type object::to() const;

Я думал сделать этот метод виртуальным в базовом классе, но шаблонные методы не могут быть виртуальными.
0
18902 / 9860 / 2410
Регистрация: 30.01.2014
Сообщений: 17,306
06.12.2018, 20:52 9
Цитата Сообщение от Юрий Ч Посмотреть сообщение
буду очень благодарен в помощи реализации
Если только вы целиком скинете что у вас получилось.

Добавлено через 57 минут
Юрий Ч, вы ссылку выше смотрели?
0
5 / 5 / 0
Регистрация: 16.05.2012
Сообщений: 178
07.12.2018, 07:15  [ТС] 10
Доброе утро!

DrOffset, Скидываю файлы проекта, архив проекта в самом низу.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#ifndef PROXY_H
#define PROXY_H
 
#include "source/writecopier.h"
 
 
class Proxy
{
  public:
    Proxy();
    virtual ~Proxy();
    virtual bool isNull() const;
    virtual std::string toString() const;
 
    // Этот код для преобразования объекта в нативный тип
    template <class T> T to() const {
      const ProxyData *data = m_data.operator->();
      return dynamic_cast<T>(*data);
    }
 
  protected:
      class ProxyData;
 
      void setData(ProxyData *data);
 
      template <class T> const T* data() const {
        return static_cast<const T*>(m_data.operator->());
      }
 
      template <class T> T* data() {
        return static_cast<T*>(m_data.operator->());
      }
 
  private:
      WriteCopier<ProxyData> m_data;
};
 
 
#endif // PROXY_H
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include "proxy.h"
#include "proxydata.h"
 
 
Proxy::Proxy()
  : m_data(nullptr)
{}
 
 
Proxy::~Proxy()
{}
 
 
bool Proxy::isNull() const
{
  return m_data->isNull();
}
 
 
std::string Proxy::toString() const
{
  return m_data->toString();
}
 
 
void Proxy::setData(ProxyData *data)
{
  m_data.reset(data);
}
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ifndef PROXYDATA_H
#define PROXYDATA_H
 
#include "proxy.h"
 
 
class Proxy::ProxyData
{
  public:
    ProxyData();
    virtual ~ProxyData();
    virtual bool isNull() const;
    virtual std::string toString() const;
};
 
 
#endif // PROXYDATA_H
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include "proxydata.h"
 
 
Proxy::ProxyData::ProxyData()
{}
 
 
Proxy::ProxyData::~ProxyData()
{}
 
 
bool Proxy::ProxyData::isNull() const
{
  return true;
}
 
 
std::string Proxy::ProxyData::toString() const
{
  return "unknown";
}
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef FLOWER_H
#define FLOWER_H
 
#include "proxy.h"
 
 
class Flower : public Proxy
{
  public:
    Flower(const std::string &name);
    virtual std::string name() const;
    virtual void rename(const std::string &name);
 
  protected:
    class ProxyData;
};
 
 
#endif // FLOWER_H
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "flower.h"
#include "flowerdata.h"
 
 
Flower::Flower(const std::string &name)
{
  setData(new Flower::ProxyData(name));
}
 
 
std::string Flower::name() const
{
  return data<Flower::ProxyData>()->name();
}
 
 
void Flower::rename(const std::string &name)
{
  data<Flower::ProxyData>()->rename(name);
}
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#ifndef FLOWERDATA_H
#define FLOWERDATA_H
 
#include "flower.h"
#include "proxydata.h"
 
 
class Flower::ProxyData : public Proxy::ProxyData
{
  public:
    ProxyData();
    ProxyData(const std::string &name);
    virtual bool isNull() const override;
    virtual std::string toString() const override;
 
    virtual std::string name() const;
    virtual void rename(const std::string &name);
 
    operator int () const;
 
  private:
    std::string m_name;
};
 
 
#endif // FLOWERDATA_H
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include "flowerdata.h"
 
 
Flower::ProxyData::ProxyData()
  : m_name("unknown")
{}
 
 
Flower::ProxyData::ProxyData(const std::string &name)
  : m_name(name)
{}
 
 
bool Flower::ProxyData::isNull() const
{
  return false;
}
 
 
std::string Flower::ProxyData::toString() const
{
  return "flower " + m_name;
}
 
 
std::string Flower::ProxyData::name() const
{
  return m_name;
}
 
 
void Flower::ProxyData::rename(const std::string &name)
{
  m_name = name;
}
 
 
Flower::ProxyData::operator int () const
{
  return 23;
}
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#ifndef WRITECOPIER_H
#define WRITECOPIER_H
 
#include <memory>
 
 
template <class T>
class WriteCopier
{
  public:
    WriteCopier(T *data)
      : m_data(data)
    {}
 
    void reset(T *data) {
      m_data.reset(data);
    }
 
    const T* operator -> () const {
      return m_data.get();
    }
 
    T* operator -> () {
      if (!m_data.unique())
        m_data.reset(new T(*m_data));
      return m_data.get();
    }
 
  private:
    std::shared_ptr<T> m_data;
};
 
 
#endif // WRITECOPIER_H
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <algorithm>
#include <iostream>
#include "source/proxy.h"
#include "source/flower.h"
#include <vector>
 
 
int main(int argc, char *argv[])
{
  Proxy rose = Flower("rose");
  std::vector<Proxy> garden;
 
  garden.push_back(std::move(rose));
  garden.push_back(Proxy());
  garden.push_back(Flower("ygiuiuyi"));
 
  /* Этот код пока не работает
  int i = rose.to<int>();
  std::cout << i << std::endl;
  */
 
  garden[1] = Flower("gladiolus");
 
  std::for_each(garden.begin(), garden.end(),
                [](const Proxy &element) {
                  std::cout << element.toString() << std::endl;
                }
  );
  return 0;
}
Вложения
Тип файла: zip test.zip (6.7 Кб, 11 просмотров)
0
18902 / 9860 / 2410
Регистрация: 30.01.2014
Сообщений: 17,306
09.12.2018, 15:43 11
Юрий Ч,

C++
1
2
3
4
5
6
7
8
9
10
11
//proxydata.cpp (в h нужно соответствующее объявление)
 
void * Proxy::ProxyData::restore(void * obj, std::type_info const & x) const
{
    if(x == typeid(std::string))
    {
        *static_cast<std::string*>(obj) = toString();
        return obj;
    }
    return nullptr;
}
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// flowerdata.cpp  (в h нужно соответствующее объявление)
 
void * Flower::ProxyData::restore(void * obj, std::type_info const & x) const
{
    if(x == typeid(std::string))
    {
        *static_cast<std::string*>(obj) = this->toString();
        return obj;
    }
    else if(x == typeid(int))
    {
        *static_cast<int *>(obj) = *this;
        return obj;
    }
    return nullptr;
}
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// proxy.h
    virtual void * restore(void * obj, std::type_info const & x) const;
 
    template <class T> T to() const {
        T obj;
        if(void * p = restore(&obj, typeid(T)))
        {
            return *static_cast<T*>(p);
        }
        throw std::bad_cast();
    }
 
// proxy.cpp
void * Proxy::restore(void * obj, std::type_info const & x) const
{
  return m_data->restore(obj, x);
}
 
// flower.cpp
void * Flower::restore(void * obj, std::type_info const & x) const
{
    return data<Flower::ProxyData>()->restore(obj, x);
}
0
09.12.2018, 15:43
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
09.12.2018, 15:43
Помогаю со студенческими работами здесь

Ошибки error C2296: -: недопустимо, левый операнд имеет тип "double (__cdecl *)(double,double,double
Думаю из-за polp #include&lt;iostream&gt; #include&lt;cmath&gt; #include&lt;cstdlib&gt; using namespace std;...

Конфликт типов (int, double, bool)
Есть простейший класс class A { public: A( long ) {} A( double ){} A( bool ) {} }...

Диапазон значений типов float, double
Добрый день! Объясните, пожалуйста, почему диапазон значений типа float (язык Си) от 3.4E–38 до...

Преобразование типов. Откуда берётся double?
Доброго времени суток всем.Объясните кто-нибудь почему компилятор в предупреждениях пишет :...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
11
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru