LINUX.ORG.RU

Перекрёстные ссылки в иерархии. Композиция. Нуб.

 , , , ,


0

2

Помогите днищу. Не могу придумать как сделать такую иерархию.

Есть набор операций, пусть будет add/sub/mul/div. Есть общий класс, который реализует представление и реализацию этих операций. И есть конкретные классы самих операций, которые реализуют и хранят уникальные данные/методы.

Как это реализовать.

namespace op {
  class op_base {//т.е. это общий класс для операций
  public:
    op_base(int a, int b) : a(a), b(b) {}
    int op();//т.е. это конкретная операция, которая как-то должна экспортироваться из каждой конкретной операции;
    operator int() {return op();}
  protected:
    int a, b;
  };
  
  class op_add {//это конкретная операция.
    int op() {return a + b};//конкретная операция
  };
  
  class op_mul {//это конкретная операция.
    int op() {return a * b};//конкретная операция
  };
  
  //в конечном итоге нам надо получить объект op_base, в котором op() бы заменялась на конкретную реализацию.
  //т.е. и 
  add(1, 2);
  //и
  mul(1, 2);
  //были объектами op_base, но в чём-то были уникальны.
};

Я уже не могу - помогите. Эти перекрёстные ссылки меня доконают.

//попытка запилить на вариенте не удалась так же, как и сотни других.

namespace variant_hell {

  struct op_add; struct op_mul;

  using op_var =  boost::variant<op_add *, op_mul *>;

  class op_visitor : public boost::static_visitor<int> {
  public:
    int operator()(op_add * op) const {return op->op();}
    int operator()(op_mul * op) const {return op->op();}
  };


  class operation {
  public:
    operation() {}
    template<typename op> operation(int a, int b, boost::mpl::identity<op>) : a(a), b(b), cop { new op{*this}} {}

    operator int() {
      return boost::apply_visitor(op_visitor(), cop);
    }

  public:
    int a, b;
  private:
    op_var cop;
  };

  struct op_add {
    op_add(operation & base) : base(base) {}
    int op() {return base.a + base.b;}
    operation & base;
  };

  struct op_mul {
    op_mul(operation & base) : base(base) {}
    int op() {return base.a * base.b;}
    operation & base;
  };


  template<typename T> struct wrapper {
    wrapper(int a, int b) : op(a, b, boost::mpl::identity<T>()) {}
    operator operation() {return op;}
    operation op;
  };

  
  auto add(int a, int b) {return wrapper<op_add>(a, b);}
}

Операции должны быть именно полноценными, отдельными классами. Класс «операция» должен быть один на всех. Класс операция - основной, который должен иметь в себе доступ к сабклассам, которые «операции».

Отдельные операций тысячи. Поэтому никакая «шаблонная база» не подходит.

Вариант с вариантом хорошо ложится на логику, но я так и не смог.

Нагинерить тонны лапши не проблема.

Может я делаю что-то не так и это делается проще?

ЯННП но наверное что-то типа того

class op_base { 
    virtual ~op_base( ) { }
    virtual void op(  ) = 0;
};

class op_mul: public op_base { 
    void op( ) override
    {
        ...
    }
};

class op_mul: public op_base { 

    void op( ) override
    { ... }
};

anonymous
()
Ответ на: комментарий от anonymous

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

Да и с виртуальными куллстори - я могу и сам. Интерфейс говно - т.е. их нельзя нормально использовать - только через указатели. Они дырявые, т.е. никаких проверок нормальных не запилить.

Вариант можно заменить на виртуальную куллстори, но это слишком убого.

registrant27492
() автор топика
Ответ на: комментарий от anonymous

нет законченной формулировки задачи и иерархии?

Я тебе не форумный васька и терпеть балабольство я не буду.

Хорошо - кукарекнул, что чего-то нету - вперёд. Пруфцы. Ищи дыры в моём объяснении.

Там всё конкретно и ясно объяснено.

registrant27492
() автор топика

Есть набор операций, пусть будет add/sub/mul/div. Есть общий класс, который реализует представление и реализацию этих операций

может лучше тег [lisp], или это такой тонкий вброс - грань меж ОО и FP как раз лежит в споре кто главнее объекты/классы или дейтсвия.

PS/ struct op_mul если это не кодогенерация - это прикольно :-)

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

или дейтсвия

это Вы о каком-то своем, особенном FP, видимо, говорите. Фапешники наоборот орут, что у них никаких действий нет.

filequest
()

Может я делаю что-то не так и это делается проще?

ага. формулируется задача. нормально формулируется, а не в стиле «я тут запилили какое-то говно непонятнодлячего но оно запилено недоконца и я ваще не знаю зачем оно мне и что дальше делать. помогите»

anonymous
()
Ответ на: комментарий от MKuznetsov

может лучше тег [lisp],

Кстати да, в динамике подобное делается как 2 пальца об асфальт, в лиспе не знаю, но в Io типа

Base := Object clone do(
   add := method(a + b)
   mull := method(a * b)
   with := method(a, b, o := self clone; o a := a; o b := b; o)
)

o1 := Base with(2, 3)
o1 mull //6
o1 add // 5
но ТС динамику не хочет:)

filequest
()
Ответ на: комментарий от filequest

в этом весь анонiмус. никуя не понял, за залез.

Кстати да, в динамике подобное делается как 2 пальца

чем твой код «вдинамике» отличается от этого кода «невдинамике»?

class op {
    int a_ = 0;
    int b_ = 0;
public:
    op( int a, int b )
        :a_(a)
        ,b_(b)
    { }
    int mul( )
    {
        return a * b;
    }
    int add( )
    {
        return a + b;
    }
};

....

op o(2, 3);
o.mul( ); // 6
o.add( ); // 6
anonymous
()
Ответ на: комментарий от MKuznetsov

может лучше тег [lisp], или это такой тонкий вброс - грань меж ОО и FP как раз лежит в споре кто главнее объекты/классы или дейтсвия.

Я ничего не знаю про ваши там ОО и фп, а лисп считаю не языком. Мне на это абсолютно покласть.

PS/ struct op_mul если это не кодогенерация - это прикольно :-)

Я же писал:

Отдельные операций тысячи.
Нагинерить тонны лапши не проблема.

Т.е. этих структурок буде тыща + естественно руками его никто писать не будет.

Если ты разбираешься - можешь помочь. Надо а) статически, б) красиво, в) мочь из себя вызывать себя(т.е. объекты внутри могут общаться с объектом, в котором они).

Это может надо сделать общую базу, потом все операции от неё наследовать и так же наследовать общую, но видя как у меня в каждом месте был затык - мне даже страшно проверять.

Я не понимаю - почему нельзя видеть таблице методов у перекрёсных ссылок? Может это можно как-то решить. Какая ему вообще разница? Я не понимаю логики этих ограничений.

Или может это можно сделать как-то по другому.

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

Там всё конкретно и ясно объяснено.

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

Хорошо - кукарекнул, что чего-то нету - вперёд. Пруфцы.

И можешь еще тон сбавить. Ну, если тебя интересуют ответы, а не троллинг.

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от filequest

Это не то. mul/add - это отдельные объекты - а результат это только часть их работы. И выглядеть это должно так:

mul(add(2, 3), 3); - потом это всё естественно экспортируется в интерфейс типов, что можно перегрузить - то будет (2 + 3) * 3.

Да и то это пример - на самом деле результат - это просто дебаг свойство и логика работы этих операций совершенно в другом.

registrant27492
() автор топика
Ответ на: комментарий от anonymous

Да плюсы не понимаю. Но если бы понял задачу, уверен, что было бы также легко:)

чем твой код «вдинамике» отличается от этого кода «невдинамике»?

Если чуть видоизменить, думаю будет видно отличие, eg

Base := Object clone do(
   c := 10
   add := method(a + b + c)
   mull := method(a * b * c)
   with := method(a, b, o := self clone; o a := a; o b := b; o)
)

o1 := Base with(1, 2)
o2 := Base with(3, 4)

o1 add  // 13
o2 add  // 17
Base c = 20
o1 add // 23
o2 add // 27

filequest
()
Ответ на: комментарий от filequest
class op {
    int a_ = 0;
    int b_ = 0;
public:

    op( int a, int b )
        :a_(a)
        ,b_(b)
    { }

    int mul( )
    {
        return a_ * b_ * c;
    }

    int add( )
    {
        return a_ + b_ + c;
    }
    static int c;
};

int op::c = 10;
....
op o1(1, 2);
op o2(3, 4);

o1.add( ); // 13 
o2.add( ); // 17

op::c = 20;

o1.add( ); // 23 
o2.add( ); // 27
anonymous
()
Ответ на: комментарий от anonymous

хз, ты конечно формально вывел то же самое, хз, че там за магия у Вас, но прочитал про

При объявлении члена класса (функции или переменной) ключевое слово static означает, что этот член класса является статическим. К статическим членам класса разрешен доступ вида: T::member. Статические фуннкции-члены класса не имеют неявного формального параметра this, не могут быть виртуальными или иметь модификатор const. Статические переменные не являются частью объектов данного класса, т.е. являются «общими» для всех объектов данного класса. Понятие статической переменной класса ближе всего к глобальным переменным C/C++ с точностью до правил доступа. В объектно ориентированной терминологии нестатические члены класса принято называть членами объекта (instance member), а статические — членами класса (class member).

Семантически это вообще не то. В моем примере, в терминах плюсов с — как раз член виртуального класса. При развитие кода это все всплывет все равно. А так, сейчас на любой мой пример ты какую-нибудь синтетику выкатишь, это все бесполезно:)

filequest
()
Ответ на: комментарий от filequest

Семантически это вообще не то

ДАТЫЧО!!! ниужели! никуясесе!

При развитие кода это все всплывет все равно

ага, ага. старая сказка про то как круг рисовать, да да.

А так, сейчас на любой мой пример ты какую-нибудь синтетику выкатишь, это все бесполезно:)

анонiмус так них и не начал понимать. Q.E.D.

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

Там вообще мало что по делу сказано - описанга зафейлившаяся идея реализации чего-то непонятного.

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

Так что если хочешь, чтобы тебе помогли - опиши решаемую задачу.

Это слишком сложно. Ну давай попробую.

//как я уже говорил, предположим наличие 4-х операций: add/sub/mul/div.

//они должны выглядеть как функции:
result = add(a, b);
//так же это должен быть контейнер с метаинфой к данной операции + 
реализация доступа к ним. У каждой операции много свойств - эти 
свойства зависят от окружения.

result = add(mul(a, b), c);
//это должно быть дерево, т.е. в add передаётся операция и c, а в  ту операцию а и б.
//и самая операция должна мочь иметь в себе саму себя.
//при этом эта вложенная, должна иметь доступ к верху. 

//Т.е. это просто декларация чего-то типа ast.
return a + b * c;//возвращает дерево.

//далее над этим уже строиться вся фигня. там много всего, но основная идея думаю понятна.

//почему каждая операция должна быть один объектом:

auto f(op, op)//сюда должно быть можно передать всё что угодно. Безо всякой лапши.
registrant27492
() автор топика
Ответ на: комментарий от anonymous

я тут запилили какое-то говно непонятнодлячего но оно запилено недоконца

Оно запилено до конца - просто не работает из-за каких-то неведомых особенностей языка, которые не поддаются логики.

Часть вопроса заключается в подвопросе «почему это так и как это победить?».

я ваще не знаю зачем оно мне и что дальше делать. помогите

Если я описываю что-то - значит оно так и должно быть. Если то, как я это описал - это не реализуется и кроссылки не победить - я подумаю как это сделать по другому. Да и вообще я описал нормально задачу - если тебе что-то не нравиться - ты спрашиваешь «зачем ты поставил это условие», либо «я не понял».

А не ноешь, ибо на нытьё я делаю только один вывод - ты не читал, понять не пытался, тебе это не интересно и ты просто засираешь мне тему.

registrant27492
() автор топика
Ответ на: комментарий от filequest

Но если бы понял задачу, уверен, что было бы также легко:)

Выше описал.

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

То есть какое-то древовидное представление какой-то программы. Pattern matching или шаблон Visitor (с виртуальными функциями), но в обоих случаях данные хранятся отдельно от операций, а не как у тебя.

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

Царь в ООП ударился??? АХАХАХАХАХАХАХА!!!

Нет. Я ударился в интерфейсы.

А ООП - это ограничение плюсов. Я не могу сделать отдельную таблицу методов, не могу сделать struct a {struct b;} - т.е. импортировать поля из другой структуры, хотя это вроде и есть наследование.

Т.е. я представляю десятки средств в языке, которые позволяют это сделать и сделать просто - почему их нет - мне не ясно. Хотя вроде во всяких рустах это уже есть.

registrant27492
() автор топика
Ответ на: комментарий от anonymous

Ладно, раз не понял словами, объясню в коде в чем разница

Base := Object clone do(
   c := 10
   add := method(a + b + c)
   mull := method(a * b * c)
   with := method(a, b, o := self clone; o a := a; o b := b; o)
)

o1 := Base with(1, 2)
o2 := Base with(3, 4)

o1 add  // 13
o2 add  // 17
Base c = 20
o2 c := 30
o1 add // 23
o2 add // 37

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

Pattern matching

Тонны кастылей.

шаблон Visitor (с виртуальными функциями)

Это и вроде запилено.

Но опять же - с виртуальными функциями - это фу. Да и как хранить? Указатель? Зачем мне эти заморочки с памятью?

А как быть с доступом выше/ниже? Городить тонну кастылей?

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

Я про это писал выше. Но это должны быть не только данные.

Только ты там не думай - я не тралю, но всё же: Как всё сложно - думал ООП - вся фигня. Уже всё есть, а хрен - запилить ООП сложнее хелворда сложнее чем, сделать то же самое на сишке.

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

Pattern matching

Тонны кастылей.

Даже не знаю, что лучше - «костыли» (это слово пишется через «о») или чтобы ничего не работало, как у тебя сейчас.

шаблон Visitor (с виртуальными функциями)

Это и вроде запилено.

После op_base, который содержит аргументы операции, я перестал читать. Операции отдельно, данные отдельно; вычисления выполняются функцией interpret, которая в случае pattern matching яляется большим switch, а в случае visitor - циклом node = operand_stack->visit(node)

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от registrant27492

Честно говоря, я так и не понял. Это что-то, видимо, ++специфическое. Например,

в add передаётся операция и c, а в ту операцию а и б.

Почему бы не передавать отдельно операцию (функцию) и аргументы, типа add(mul, a, b, c) — это была бы обычная композиция.

result = add(mul(a, b), c);

тут в result что должно попасть?

filequest
()
Ответ на: комментарий от filequest

Честно говоря, я так и не понял. Это что-то, видимо, ++специфическое. Например,

А зачем мне язык, который не может в перегрузку операторов?

Почему бы не передавать отдельно операцию (функцию) и аргументы

Понимаешь в чём дело - реализация подстраивается под интерфейс, а не наоборот. Это должно быть удобно и красиво, иначе зачем оно нужно? Я это точно так же могу запилить и на сишке. add(mul, a, b, c)

Мне и нужно от язык фичи, которые превратят это в a + b * c, a *=b, a &= b, a & b, a == b и прочее. Всё должно выглядеть нормально.

тут в result что должно попасть?

В зависимости от того, что такое результат, но в общем случае составная операция. Т.е. дерево из операций.

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

Даже не знаю, что лучше - «костыли» (это слово пишется через «о») или чтобы ничего не работало, как у тебя сейчас.

Я описал причину по которой оно не работает. Я в этом не виноват. Я знаю как пишется это слово.

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

После op_base, который содержит аргументы операции, я перестал читать.

В этом и проблема.

Операции отдельно, данные отдельно; вычисления выполняются , которая в случае pattern matching яляется большим switch, а в случае visitor - циклом node = operand_stack->visit(node)

Там не вычисления - там целый компилятор. Т.е. программа порождает компилятор программы, который при исполнение генерирует уже исполняемую программу, а вернее множество.

Я же убьюсь с этими кОстылями - ладно. Спасибо - может быть придумаю что-нибудь.

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

А зачем мне язык, который не может в перегрузку операторов?

А зачем костыли в языке, где без них все работает?:)

Это должно быть удобно и красиво, иначе зачем оно нужно? Я это точно так же могу запилить и на сишке. add(mul, a, b, c) Мне и нужно от язык фичи, которые превратят это в a + b * c, a *=b, a &= b, a & b, a == b и прочее. Всё должно выглядеть нормально.

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

"foo" ++
list(1, 2, 3) ++
object ++
Но твой случай представляется усложеннием на пустом месте. Сахарность интерфейса не должно быть самоцелью, наоборот, он должен быть максимально простым, универсальным и масштабируемым, это не Io-way'но, так же как и макросы.

filequest
()
Ответ на: комментарий от filequest

А зачем костыли в языке, где без них все работает?:)

Сделай.

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

Сделай.

Но твой случай представляется усложеннием на пустом месте.

Твоя проблема. Мой случай такой, каким он должен быть. Выше я объяснял почему.

Сахарность интерфейса не должно быть самоцелью

Это не сахарность интерфейса - это интерфейс. Он должен быть вменяемый и это цель интерфейса. У интерфейса есть один критерий - это эргономика.

Если этот интерфейс реализует язык - он должен его реализовать полностью.

он должен быть максимально простым, универсальным и масштабируемым, это не Io-way'но, так же как и макросы.

Меня это не волнует. Хотя перегрузка операторов и есть «максимально простым, универсальным». add(mul 1 2 3) - это неведомая херня 1 * 2 + 3 - это «просто и универсальный» интерфейс, понятный любому школьнику.

registrant27492
() автор топика

После многолетних трудов администрации ЛОР наконец-то пришел к своему каноническому виду: царь, анонiмус и пара дохлых мимокрокодилов.

anonymous
()
Ответ на: комментарий от registrant27492

Там не вычисления - там целый компилятор.

Вычислить или сгенерировать вычисляющий код - разница невелика.

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

Что перегрузку сделать? А нахер она нужна, если можно тупо

m := method(x,
  if(x kindOf(Foo), return bar) 
  if(x kindOf(Bar), return baz)
)
точно также и по сигнатуре можно. Можно сделать такую «перегрузку», которая сишникам и не снилась. И при этом память не будет засираться. В статике перегрузка — это обход ограничений, создаваемых тайпчекером

У интерфейса есть один критерий - это эргономика.

Может с точки зрения сишного кодера это и так, а с точки зрения ООП основными критериями является то что я сказал — простота, универсальность, масштабируемость. Экономить на спичках тут не принято, пустое усложнение всегда выходит боком.

понятный любому школьнику.

школьнику может и понятно, а вот программисту понятно другое, что операторы — это вообще зло. В нормальном ЯП я должен иметь возможность писать list(1, 2, 3) reduce(+), то есть, + должен быть обычным методом, а не оператором.

filequest
()
Ответ на: комментарий от registrant27492

Я когда-то обмазался одной такой шаблонной чертовщиной: http://www.artima.com/cppsource/cooperative_visitor.html

Для изучения тёмных мест плюсов очень полезно, но на практике куда лучше просто генерирующий лапшу скрипт на питоне накидать.

anonymous
()

Я внимательно прочел тред и стартовый пост

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

Это покроет и работу с деревом, и представление операций в виде отдельных классов и красоту и изящность.

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

impfp
()

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

Ты ничего не понял - о чём я говорил и что требуется. Зачем пишешь? Я уже задачу описывал.

Это покроет и работу с деревом, и представление операций в виде отдельных классов и красоту и изящность.

Нет. Твои представления меня мало волнуют. Выше уже писал a * b + c - в студию.

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

Меня твои представления мало волнуют - повторю ещё раз. Я лучше знаю что мне надо, чем левый нулёвый адепт. Сводить мои задачи к тем лабам, что ты ваял за партой - глупо.

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

registrant27492
() автор топика

но я так и не смог.

Все потому что:

Нуб.

Может я делаю что-то не так и это делается проще?

Естественно! Тебе уже несколько вариантов рассказали, а ты всё тупишь.

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