LINUX.ORG.RU

Как в C++ принято скрывать детали реализации?

 ,


0

6

Проблема: проект конпелируется в лучших традициях по 15 минут в 8 потоков. Изи солюшн: перестать писать код в хидерах и начать инстанциировать в cpp — это все и собаке понятно.

Но вот при попытке сделать так натыкаюсь на грабли: объекты соержат данные, объявления которых приводят к жуткой шаблонной оргии. Хочется а) сохранить статичный интерфейс (никаких virtual = 0); б) не связываться с pimpl, ибо уродство.

В любимой сишечке все делается просто и элегантно:

#ifndef LIBRARY_SOURCE
typedef void *INTERFACE;
#endif

INTERFACE* interface_new(int param1, int param2);
void inferface_free(INTERFACE *iface);
int interface_method1(INTERFACE *iface, int arg);
/* etc */
В C++ это смотрится вроде как убого, потому что можно вызывать статические методы через точку или стрелочку, но объявить такой объект в хидере нельзя.

pimpl убого по определению и даже не рассматривается, абстрактный базовый класс связывает по рукам и ногам и вообще заставляет писать лишний и ненужный код. Плюнуть на крестоахинею и писать как диды завещали (бонусом сделать все extern C и наслаждаться изи биндингами ко всему, что движется), или есть что-то чего я не знаю?

P. S. самое близкое, но убогое — это

// header
struct Interface {
protected:
public:
  Interface() = delete;
  Interface(const Interface&) = delete;
  ~Interface();

  int method1(int arg1, int arg2);
  static Interface *create(int param1, int param2);
}

// cpp
struct Implementation : public Interface {
  CTemplate<is<shit>,and<urine>> field;
  /* пихаем всю дейту */
  Implementation(int param1, int param2) { ... }
  int method1(int arg1, int arg2) { ... }
};

Interface* Interface::create(int param1, int param2) {
  return new Implementation(param1, param2);
}

/* извращенный pimpl, можно было бы напрямую работать с this, интерпретируя его как Implementation */
int Interface::method1(int arg1, int arg2)
{
  return reinterpret_cast<Implementation*>(this)->method1(arg1, arg2);
}

/* вишенка на торте: бесконечная рекурсия, как я люблю, х-з че делать */
Interface::~Interface()
{
  reinterpret_cast<Implementation*>(this)::~Implementation();
}

Че делать-то?

Перемещено leave из desktop

не связываться с pimpl, ибо уродство

Ещё причины есть? Потому что Qt успешно использует pimpl, и код у них весьма приятный, как и проект в целом.

anonymous00 ★★
()

Убрать Implementation, написать весь код в Interface, конструктор объявить в хедере приватным.

iliyap ★★★★★
()

Почему бы просто не декларивотать типы параметров в текущем классе не и не тянуть все в хедерах. Например вместо Painter.hpp:

#include <Line.hpp>
#include <Circle.hpp>

class Painter {
public:
  void draw(Line *l);
  void draw(const Circle &c);
}

Написать:

class Line;
class Circle;

class Painter {
public:
  void draw(Line *l);
  void draw(const Circle &c);
}

Также хорошо помогает разбиение большого проекта на библиотеки, в каждой библиотеке выдиляется минимальный набор «публичных» хедеров, реализация - прячится (либо через декларирование, либо через проксирование - например как в QT).

Ну и самый простой но довольно эффективный - использовать прекомпилед хедеры.

zaz ★★★★
()

pimpl дорого, медленно и вырвиглазно, stack-only экземпляры вообще не сделать без хаков с placement new. Зато вот блоггер вася (TM) пишет что abstract base class для пацанов https://skyfromme.wordpress.com/2015/07/18/blast-from-the-past-the-pimpl/. Он и эффективнее (см. тест), и boilerplate меньше и вообще ня~н.

d_a ★★★★★
()
Ответ на: комментарий от anonymous00

Потому что Qt успешно использует pimpl, и код у них весьма приятный, как и проект в целом.

Мой личный опыт разработки в коммерческой компании подсказывает что Qt это не про эффективность от слова совсем. Qt это про обратную бинарную совместимость, с ней у них действительно всё хорошо благодаря pimpl.

d_a ★★★★★
()

зачем тебе это постит, на этом форуме, учитывая что тут за открытый исходный код

enep ★★★★★
()
Ответ на: комментарий от RazrFalcon

Лечиться?

лорчанка проходит с возрастом. но не у всех. баны должны решить проблему

mos ★★☆☆☆
()
Ответ на: комментарий от iliyap

Проблема в том, что код требует некоторых данных, вытаскивать наружу дорого.

kawaii_neko ★★★★
() автор топика
Ответ на: комментарий от d_a

pimpl дорого, медленно и вырвиглазно, stack-only экземпляры вообще не сделать

При помощи трюка с INTERFACE из хедпоста stack only тоже сделать невозможно.

tailgunner ★★★★★
()
Ответ на: комментарий от zaz

Также хорошо помогает разбиение большого проекта на библиотеки, в каждой библиотеке выдиляется минимальный набор «публичных» хедеров, реализация - прячится

Ну, собственно, вопрос как раз об этом сокрытии.

kawaii_neko ★★★★
() автор топика
Ответ на: комментарий от tailgunner

stack-only вообще не сделать, не зная размера объекта, а тут он как раз неизвестен. Конечно, можно генерировать интерфейсы из implementation в виде

class Interface {
  char space[SIZE_OF_IMPLEMENTATION];
public:
  int method1(int arg1, int arg2);
};
Но за такое можно и по интерфейсу выхватить.

kawaii_neko ★★★★
() автор топика
reinterpret_cast<Implementation*>(this)

static_cast! упырь!

а вообще для этого есть COM:

#include <typeindex>
class Foo : ..., public BBBB {
    void * get(std::type_index type);
    AAAA aaaa;
}

void * Foo::get(std::type_index type) {
    static std::type_index _AAAA(typeid(AAAA));
    static std::type_index _BBBB(typeid(BBBB));
    if (type == _AAAA) return &aaaa;
    if (type == _BBBB) return static_cast<BBBB*>(this);
    return nullptr;
}

ckotinko ☆☆☆
()
Ответ на: комментарий от kawaii_neko

Ну, собственно, вопрос как раз об этом сокрытии.

class MyPrivateClass;

class MyPublicClass
{
private:
  MyPrivateClass *m_impl;

public:
  void f1();
}
#include "MyPublicClass.hpp"
#include "MyPrivateClass.hpp"

MyPublicClass::f1()
{
  m_impl->f1();
}
zaz ★★★★
()
Ответ на: комментарий от kawaii_neko

stack-only вообще не сделать, не зная размера объекта

Да, Капитан. Поэтому непонятно, почему эта претензия предъявляется pimpl, но не твоему коду.

tailgunner ★★★★★
()

конпелируется
хидерах
диды

Че делать-то?

для начала перестать заменять в словах «м» на «н», «и» на «е», а также «э» и «е» на «и»

dt1 ★★
()

абстрактный базовый класс связывает по рукам и ногам и вообще заставляет писать лишний и ненужный код

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

Поправил, не благодари.

hobbit ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.