Как такое написать по Сиплюсплюсному?
От: kov_serg Россия  
Дата: 21.03.25 07:20
Оценка:
Как такое написать по Сиплюсплюсному что бы без UB

#include <stdio.h>

template<class A,class B>A*parent_of(B*self,B A::*t) { 
    return (A*)((char*)self-(char*)(&(((A*)0)->*t)));
}
template<class A,class B>A*parent_of(B*self,B(A::*t)[1]) { 
    return (A*)((char*)self-(char*)(&(((A*)0)->*t)));
}

struct I1 { virtual void show()=0; };
struct I2 { virtual void print()=0; };

struct A {
    int x[3];
    struct :I1 {
        A* parent() { return parent_of(this,&A::i1); }
        void show() { printf("show x0=%d\n",parent()->x[0]); }
    } i1[1];
    struct :I2 {
        A* parent() { return parent_of(this,&A::i2); }
        void print() { printf("print x0=%d\n",parent()->x[0]); }
    } i2[1];
};

void fn(I1* i) { i->show(); }
void fn(I2* i) { i->print(); }

int main(int argc, char **argv) {
    A a[1];
    a->x[0]=123;
    fn(a->i1);
    fn(a->i2);
    return 0;
}
Re: Как такое написать по Сиплюсплюсному?
От: rg45 СССР  
Дата: 21.03.25 12:04
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Как такое написать по Сиплюсплюсному что бы без UB


Никак. То, что ты сделал — это offsetof, только через... ну ты понял. А offsetof без UB можно использовать только с POD типами (standard layout, как минимум).
--
Справедливость выше закона. А человечность выше справедливости.
Re[2]: Как такое написать по Сиплюсплюсному?
От: kov_serg Россия  
Дата: 21.03.25 12:10
Оценка:
Здравствуйте, rg45, Вы писали:

R>Никак. То, что ты сделал — это offsetof, только через... ну ты понял. А offsetof без UB можно использовать только с POD типами (standard layout, как минимум).


Хочется вложенных классов реализующих интерфейс, как innerClass в java, но без лишних указателей. Неужели великий и ужасный c++ так не может?
Re[3]: Как такое написать по Сиплюсплюсному?
От: rg45 СССР  
Дата: 21.03.25 12:17
Оценка: +3
Здравствуйте, kov_serg, Вы писали:

_>Хочется вложенных классов реализующих интерфейс, как innerClass в java, но без лишних указателей. Неужели великий и ужасный c++ так не может?


А для тех, кто пишет на C++ как на java, никто удобств не обещал.
--
Справедливость выше закона. А человечность выше справедливости.
Re: Как такое написать по Сиплюсплюсному?
От: B0FEE664  
Дата: 22.03.25 19:32
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Как такое написать по Сиплюсплюсному что бы без UB

  Скрытый текст
_>
_>#include <stdio.h>

_>template<class A,class B>A*parent_of(B*self,B A::*t) { 
_>    return (A*)((char*)self-(char*)(&(((A*)0)->*t)));
_>}
_>template<class A,class B>A*parent_of(B*self,B(A::*t)[1]) { 
_>    return (A*)((char*)self-(char*)(&(((A*)0)->*t)));
_>}

_>struct I1 { virtual void show()=0; };
_>struct I2 { virtual void print()=0; };

_>struct A {
_>    int x[3];
_>    struct :I1 {
_>        A* parent() { return parent_of(this,&A::i1); }
_>        void show() { printf("show x0=%d\n",parent()->x[0]); }
_>    } i1[1];
_>    struct :I2 {
_>        A* parent() { return parent_of(this,&A::i2); }
_>        void print() { printf("print x0=%d\n",parent()->x[0]); }
_>    } i2[1];
_>};

_>void fn(I1* i) { i->show(); }
_>void fn(I2* i) { i->print(); }

_>int main(int argc, char **argv) {
_>    A a[1];
    a->>x[0]=123;
_>    fn(a->i1);
_>    fn(a->i2);
_>    return 0;
_>}
_>



#include <stdio.h>

struct I1 
{ 
    virtual void show()=0; 
};
struct I2 
{ 
    virtual void print()=0; 
};

struct A : I1,I2
{
    int x[3];
    void show() { printf("show x0=%d\n",x[0]); }
    void print() { printf("print x0=%d\n",x[0]); }
    I1*  i1 = { this };
    I2*  i2 = { this };
};

void fn(I1* i) { i->show(); }
void fn(I2* i) { i->print(); }

int main(int argc, char **argv) {
    A a[1];
    a->x[0]=123;
    fn(a->i1);
    fn(a->i2);
    return 0;
}

И каждый день — без права на ошибку...
Re[2]: Как такое написать по Сиплюсплюсному?
От: rg45 СССР  
Дата: 22.03.25 20:03
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Здравствуйте, kov_serg, Вы писали:


_>>Как такое написать по Сиплюсплюсному что бы без UB

BFE>
  Скрытый текст
_>>
_>>#include <stdio.h>

_>>template<class A,class B>A*parent_of(B*self,B A::*t) { 
_>>    return (A*)((char*)self-(char*)(&(((A*)0)->*t)));
_>>}
_>>template<class A,class B>A*parent_of(B*self,B(A::*t)[1]) { 
_>>    return (A*)((char*)self-(char*)(&(((A*)0)->*t)));
_>>}

_>>struct I1 { virtual void show()=0; };
_>>struct I2 { virtual void print()=0; };

_>>struct A {
_>>    int x[3];
_>>    struct :I1 {
_>>        A* parent() { return parent_of(this,&A::i1); }
_>>        void show() { printf("show x0=%d\n",parent()->x[0]); }
_>>    } i1[1];
_>>    struct :I2 {
_>>        A* parent() { return parent_of(this,&A::i2); }
_>>        void print() { printf("print x0=%d\n",parent()->x[0]); }
_>>    } i2[1];
_>>};

_>>void fn(I1* i) { i->show(); }
_>>void fn(I2* i) { i->print(); }

_>>int main(int argc, char **argv) {
_>>    A a[1];
    a->>>x[0]=123;
_>>    fn(a->i1);
_>>    fn(a->i2);
_>>    return 0;
_>>}
_>>




BFE>
BFE>#include <stdio.h>

BFE>struct I1 
BFE>{ 
BFE>    virtual void show()=0; 
BFE>};
BFE>struct I2 
BFE>{ 
BFE>    virtual void print()=0; 
BFE>};

BFE>struct A : I1,I2
BFE>{
BFE>    int x[3];
BFE>    void show() { printf("show x0=%d\n",x[0]); }
BFE>    void print() { printf("print x0=%d\n",x[0]); }
BFE>    I1*  i1 = { this };
BFE>    I2*  i2 = { this };
BFE>};

BFE>void fn(I1* i) { i->show(); }
BFE>void fn(I2* i) { i->print(); }

BFE>int main(int argc, char **argv) {
BFE>    A a[1];
    a->>x[0]=123;
BFE>    fn(a->i1);
BFE>    fn(a->i2);
BFE>    return 0;
BFE>}
BFE>

BFE>

А потом так хоба:

http://coliru.stacked-crooked.com/a/9771187b1c11b3b6

    A b = a[0];
    b.x[0] = 456;
    fn(b.i1); // show x0=123
    fn(b.i2); // print x0=123


Хлопотно это, автореференсные объекты. И некрасиво.
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 22.03.2025 20:04 rg45 . Предыдущая версия .
Re[2]: Как такое написать по Сиплюсплюсному?
От: kov_serg Россия  
Дата: 22.03.25 20:07
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>
BFE>#include <stdio.h>

BFE>struct I1 
BFE>{ 
BFE>    virtual void show()=0; 
BFE>};
BFE>struct I2 
BFE>{ 
BFE>    virtual void print()=0; 
BFE>};

BFE>struct A : I1,I2
BFE>{
BFE>    int x[3];
BFE>    void show() { printf("show x0=%d\n",x[0]); }
BFE>    void print() { printf("print x0=%d\n",x[0]); }
BFE>    I1*  i1 = { this };
BFE>    I2*  i2 = { this };
BFE>};

BFE>void fn(I1* i) { i->show(); }
BFE>void fn(I2* i) { i->print(); }

BFE>int main(int argc, char **argv) {
BFE>    A a[1];
    a->>x[0]=123;
BFE>    fn(a->i1);
BFE>    fn(a->i2);
BFE>    return 0;
BFE>}
BFE>

BFE>
Так можно было просто указатели преобразовать. Не заводя отдельных полей.
I1* get_i1() { return this; }

Хочется чего-то более структурированного

Слегка усложним. А если так:
#include <stdio.h>

template<class A,class B>A*parent_of(B*self,B A::*t) { 
    return (A*)((char*)self-(char*)(&(((A*)0)->*t)));
}
template<class A,class B>A*parent_of(B*self,B(A::*t)[1]) { 
    return (A*)((char*)self-(char*)(&(((A*)0)->*t)));
}

struct I1 { virtual void show()=0; };
struct I2 { virtual void print()=0; };
struct I3 { virtual void next()=0; virtual void print()=0; };

struct A {
    int x[3];
    struct :I1 {
        A* parent() { return parent_of(this,&A::i1); }
        void show() { printf("show x0=%d\n",parent()->x[0]); }
    } i1[1];
    struct :I2 {
        A* parent() { return parent_of(this,&A::i2o); }
        void print() { printf("print  x0=%d\n",parent()->x[0]); }
    } i2o[1];    
    struct :I2 {
        A* parent() { return parent_of(this,&A::i2); }
        void print() { printf("print2 x0=%d\n",parent()->x[0]); }
    } i2[1];
    struct :I3 {
        A* parent() { return parent_of(this,&A::i3); }
        void next() { printf("next3\n"); }
        void print() { printf("print3 x0=%d\n",200-parent()->x[0]); }
    } i3[1];
};

void fn(I1* i) { i->show(); }
void fn(I2* i) { i->print(); }
void fn(I3* i) { i->print(); }

int main(int argc, char **argv) {
    A a[1];
    a->x[0]=123;
    fn(a->i1);
    fn(a->i2);
    fn(a->i2o);
    fn(a->i3);
    return 0;
}
Отредактировано 22.03.2025 20:14 kov_serg . Предыдущая версия .
Re[3]: Как такое написать по Сиплюсплюсному?
От: B0FEE664  
Дата: 24.03.25 08:29
Оценка:
Здравствуйте, kov_serg, Вы писали:

  Скрытый текст
BFE>>
BFE>>#include <stdio.h>

BFE>>struct I1 
BFE>>{ 
BFE>>    virtual void show()=0; 
BFE>>};
BFE>>struct I2 
BFE>>{ 
BFE>>    virtual void print()=0; 
BFE>>};

BFE>>struct A : I1,I2
BFE>>{
BFE>>    int x[3];
BFE>>    void show() { printf("show x0=%d\n",x[0]); }
BFE>>    void print() { printf("print x0=%d\n",x[0]); }
BFE>>    I1*  i1 = { this };
BFE>>    I2*  i2 = { this };
BFE>>};

BFE>>void fn(I1* i) { i->show(); }
BFE>>void fn(I2* i) { i->print(); }

BFE>>int main(int argc, char **argv) {
BFE>>    A a[1];
    a->>>x[0]=123;
BFE>>    fn(a->i1);
BFE>>    fn(a->i2);
BFE>>    return 0;
BFE>>}
BFE>>

BFE>>
_>Так можно было просто указатели преобразовать. Не заводя отдельных полей.
_>
_>I1* get_i1() { return this; }
_>

_>Хочется чего-то более структурированного

_>Слегка усложним. А если так:

_>
_>#include <stdio.h>

_>template<class A,class B>A*parent_of(B*self,B A::*t) { 
_>    return (A*)((char*)self-(char*)(&(((A*)0)->*t)));
_>}
_>template<class A,class B>A*parent_of(B*self,B(A::*t)[1]) { 
_>    return (A*)((char*)self-(char*)(&(((A*)0)->*t)));
_>}

_>struct I1 { virtual void show()=0; };
_>struct I2 { virtual void print()=0; };
_>struct I3 { virtual void next()=0; virtual void print()=0; };

_>struct A {
_>    int x[3];
_>    struct :I1 {
_>        A* parent() { return parent_of(this,&A::i1); }
_>        void show() { printf("show x0=%d\n",parent()->x[0]); }
_>    } i1[1];
_>    struct :I2 {
_>        A* parent() { return parent_of(this,&A::i2o); }
_>        void print() { printf("print  x0=%d\n",parent()->x[0]); }
_>    } i2o[1];    
_>    struct :I2 {
_>        A* parent() { return parent_of(this,&A::i2); }
_>        void print() { printf("print2 x0=%d\n",parent()->x[0]); }
_>    } i2[1];
_>    struct :I3 {
_>        A* parent() { return parent_of(this,&A::i3); }
_>        void next() { printf("next3\n"); }
_>        void print() { printf("print3 x0=%d\n",200-parent()->x[0]); }
_>    } i3[1];
_>};

_>void fn(I1* i) { i->show(); }
_>void fn(I2* i) { i->print(); }
_>void fn(I3* i) { i->print(); }

_>int main(int argc, char **argv) {
_>    A a[1];
    a->>x[0]=123;
_>    fn(a->i1);
_>    fn(a->i2);
_>    fn(a->i2o);
_>    fn(a->i3);
_>    return 0;
_>}
_>


Тупое решение "в лоб":
#include <stdio.h>

struct I1 { virtual void show()=0; };
struct I2 { virtual void print()=0; };
struct I3 { virtual void next()=0; virtual void print()=0; };

struct A {
    int x[3];
    struct II1:I1 {
        II1(A*p):parent(p){}; A* parent=nullptr;
        void show() { printf("show x0=%d\n",parent->x[0]); }
    } i1[1]{this};
    struct II2:I2 {
        II2(A*p):parent(p){}; A* parent=nullptr;
        void print() { printf("print  x0=%d\n",parent->x[0]); }
    } i2o[1]{this};
    struct II22:I2 {
        II22(A*p):parent(p){}; A* parent=nullptr;
        void print() { printf("print2 x0=%d\n",parent->x[0]); }
    } i2[1]{this};
    struct II3:I3 {
        II3(A*p):parent(p){}; A* parent=nullptr;
        void next() { printf("next3\n"); }
        void print() { printf("print3 x0=%d\n",200-parent->x[0]); }
    } i3[1]{this};
};

void fn(I1* i) { i->show(); }
void fn(I2* i) { i->print(); }
void fn(I3* i) { i->print(); }

int main(int argc, char **argv) {
    A a[1];
    a->x[0]=123;
    fn(a->i1);
    fn(a->i2);
    fn(a->i2o);
    fn(a->i3);
    return 0;
}



А зачем так издеваться над объектно ориентированным подходом?
И каждый день — без права на ошибку...
Re[4]: Как такое написать по Сиплюсплюсному?
От: kov_serg Россия  
Дата: 24.03.25 09:08
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Тупое решение "в лоб":

Дык хотелось не заводить лишних указателей.

BFE>А зачем так издеваться над объектно ориентированным подходом?

Очень просто это указатель группу функций — переходник такой.

Идея примерно такая:
struct Listner {
    virtual void begin(void *args)=0;
    virtual int  progress(double p)=0;
    virtual void end(void *res)=0;
};
struct Worker { /*...*/ }; // постепенно исполняемая функция, иногда требующая осязаемого времени на выполнение
struct Workers { /*...*/ }; // выполняет постепенно исполняемые функции в разных потоках, полю имеет очереть сообщений для коммуникации и сигналы для аварийной связи
struct Action1Args { string query; };
struct Action1Result { string result; };
struct Action1Worker : Worker { /*...*/ }; // например db query
struct Action2Args { int a,b; };
struct Action2Result { int c,f; };
struct Action2Worker : Worker { /*...*/ }; // или какой-нибудь https request

struct GUIWindow {
    struct:Listner {
        GUIWindow* parent() { return parent_of(this,&GUIWindow::action1); }
        void begin(void *args) { parent()->action1_begin((Action1Args*)args); }
        int  progress(double p) { return parent()->action1_progress(p); }
        void end(void *res) { parent()->action1_end((Action2Result*)res); }
    } action1[1];
    struct:Listner {
        GUIWindow* parent() { return parent_of(this,&GUIWindow::action2); }
        void begin(void *args) { parent()->action2_begin((Action2Args*)args); }
        int  progress(double p) { return 0; }
        void end(void *res) { parent()->action2_end((Action2Result*)res); }
    } action2[1];
    Workers workers[1];
    Action1Worker a1w[1];
    Action2Worker a2w[1];
    enum { action1_id,action2_id };
    void init() {
        workers->set(action1_id,action1,a1w); // что-бы тут не передавать кучу функций
        workers->set(action2_id,action2,a2w); // или не делать одну но с длинным switch и кучей event codes
    };
    void done() {
        workers->done();
    }
    // ...
    void on_action1() { workers->start(action1_id); }
    void on_action1_cancel() { workers->cancel(action1_id); }
    void on_action2() { workers->start(action2_id); }
    void on_action2_cancel() { workers->cancel(action2_id); }
    void on_msg_loop() { workers->msg_loop(); } // тут собственно диспечеризируем сообщения по функциям (в основном потоке)
    // ...
    void enable_action1(bool en) {
        btn_action1.Enable(en);
        btn_action1_cancel.Enable(!en);
        progress.Show(!en);
    }
    void action1_begin(Action1Args *args) {
        args->query=edQuery.Text;
        enable_action1(false);
    }
    int action1_progress(double p) {
        action1_progress.SetProgess(p);
        return 0;
    }
    void action1_end(Action1Result *res) {
        edResult.SetText(res->result);
        enable_action1(true);
    }
    void action2_begin(Action2Args *args) {
        args->a=10;
        args->b=22;
        action2_spinner.Active(true);
    }
    void action2_end(Action2Result *res) {
        lbResult=fmt("result=%d",res->c);
        action2_spinner.Active(false);
    }
    //...
};
Отредактировано 24.03.2025 10:56 kov_serg . Предыдущая версия . Еще …
Отредактировано 24.03.2025 10:51 kov_serg . Предыдущая версия .
Отредактировано 24.03.2025 9:53 kov_serg . Предыдущая версия .
Отредактировано 24.03.2025 9:11 kov_serg . Предыдущая версия .
Отредактировано 24.03.2025 9:09 kov_serg . Предыдущая версия .
Re[5]: Как такое написать по Сиплюсплюсному?
От: B0FEE664  
Дата: 24.03.25 11:07
Оценка:
Здравствуйте, kov_serg, Вы писали:

BFE>>Тупое решение "в лоб":

_>Дык хотелось не заводить лишних указателей.
Это в прошлом веке имело иногда смысл экономить на указателях, а современном мире экономить на указателях — это себя не уважать.

BFE>>А зачем так издеваться над объектно ориентированным подходом?

_>Очень просто это указатель группу функци — переходник такой.
Ну так и зачем эти извращения?
Если вам нужен указатель на группу функций, ну так и заведите группу функций и возьмите от неё указатель.
struct Listner
{
  std::function<void(void *args)> begin;
  std::function<int(double p)> progress;
  std::function<void(void *args)> end;
};


struct GUIWindow {
...
  // это объявление писать в конце класса 
  Listner action1 = 
  {
     .begin    = std::function<void(void *args)>( [pThis = this](void *args){        pThis->action1_begin((Action1Args*)args);}),
     .progress = std::function<int(double p)   >( [pThis = this](double p)  { return pThis->action1_progress(p);}),
     .end      = std::function<void(void *args)>( [pThis = this](void *args){        pThis->action1_end((Action2Result*)res);}),
  };
};


А вообще, здесь где-то нужен полиморфизм вида signal-slot (или шаблонные виртуальные функции, которые в C++ все еще не могут добавить из-за косности мышления)
И каждый день — без права на ошибку...
Re[6]: Как такое написать по Сиплюсплюсному?
От: kov_serg Россия  
Дата: 24.03.25 11:16
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Ну так и зачем эти извращения?

BFE>Если вам нужен указатель на группу функций, ну так и заведите группу функций и возьмите от неё указатель.
BFE>
BFE>struct Listner
BFE>{
BFE>  std::function<void(void *args)> begin;
BFE>  std::function<int(double p)> progress;
BFE>  std::function<void(void *args)> end;
BFE>};


BFE>struct GUIWindow {
BFE>...
BFE>  // это объявление писать в конце класса 
BFE>  Listner action1 = 
BFE>  {
BFE>     .begin    = std::function<void(void *args)>( [pThis = this](void *args){        pThis->action1_begin((Action1Args*)args);}),
BFE>     .progress = std::function<int(double p)   >( [pThis = this](double p)  { return pThis->action1_progress(p);}),
BFE>     .end      = std::function<void(void *args)>( [pThis = this](void *args){        pThis->action1_end((Action2Result*)res);}),
BFE>  };
BFE>};
BFE>


BFE>А вообще, здесь где-то нужен полиморфизм вида signal-slot (или шаблонные виртуальные функции, которые в C++ все еще не могут добавить из-за косности мышления)

В C было просто достаточно двух указателей и кучи констант и никаких тебе UB

Вариант с лямбдами в std::function ещё более ужасен (на мой взгляд) чем другие альтернативы
Мой вариант работает достаточно хорошо и локанично, не смотря на то что это не достаточно сиплюсплюснуто.
А как написать parent_of следуя букве е6@нyтblх современных стандартов, я хз.
Отредактировано 24.03.2025 11:17 kov_serg . Предыдущая версия .
Re[7]: Как такое написать по Сиплюсплюсному?
От: B0FEE664  
Дата: 24.03.25 14:19
Оценка:
Здравствуйте, kov_serg, Вы писали:

BFE>>А вообще, здесь где-то нужен полиморфизм вида signal-slot (или шаблонные виртуальные функции, которые в C++ все еще не могут добавить из-за косности мышления)

_>В C было просто достаточно двух указателей и кучи констант и никаких тебе UB
До тех пор, пока в арифметике операций с указателями не ошибся.

_>Вариант с лямбдами в std::function ещё более ужасен (на мой взгляд) чем другие альтернативы

Если применять патерны C на С++, то так и получается.
А вот если использовать нормальный патерн, вроде signal-slot или, там std::future / std::async / std::promise, то код будет выглядеть совсем иначе.

_>Мой вариант работает достаточно хорошо и локанично, не смотря на то что это не достаточно сиплюсплюснуто.

Вариант, как вариант, видал я и позабористей. А вот то, что хорош — нет: если GUIWindow умрет раньше Workers, то это "всё", "падаем".

_>А как написать parent_of следуя букве е6@нyтblх современных стандартов, я хз.

Известно как — запомнить указатель и вернуть его.
Хотите экономить на спичках, пишите в C стиле: без виртуальности и наследований.
И каждый день — без права на ошибку...
Re[6]: Как такое написать по Сиплюсплюсному?
От: kov_serg Россия  
Дата: 24.03.25 15:32
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE> или шаблонные виртуальные функции, которые в C++ все еще не могут добавить из-за косности мышления


А для чего они могут понадобиться? И как это должно выглядеть?

Я вообще считаю то что C++ иногда скрывает слишком много деталей там где это не нужно. И вводит избыточные понятия там где совершенно не нужно.
Например таблицы виртуальных функций, состояние корутин, вводит не предсавимые значения для типов (на пример если байт это 8бит то в C++ он имеет 257 значений), который существуют паралельно с типом (pointer provenance), делает выводы из ложных предположений. Вообще иногда творит дичь, прикрываясь тем что это дозволено по корану стандарту и никак иначе пророк не велит поступать никак нельзя иначе оптимизировать не получится.
Re[8]: Как такое написать по Сиплюсплюсному?
От: kov_serg Россия  
Дата: 24.03.25 15:46
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>До тех пор, пока в арифметике операций с указателями не ошибся.

А чего бы это. Если не нравятся указатели всегда можно перейти к handles(ухватам) они гарантированно могут быть валидированы в любой момент.

BFE> Если применять патерны C на С++, то так и получается.

BFE>А вот если использовать нормальный патерн, вроде signal-slot или, там std::future / std::async / std::promise, то код будет выглядеть совсем иначе.
signal-slot как в qt забабахав предкомпиляцию, что бы весь треш спрятать под ковёр.

futute/promise никаких гарантий не дают и общаться с рабочим потоком прдётся через свои прослойки. Если можете показать как это элегантно сделать — покажите.


BFE>Вариант, как вариант, видал я и позабористей. А вот то, что хорош — нет: если GUIWindow умрет раньше Workers, то это "всё", "падаем".

нет. Тут как раз при убийстве GUIWindow вызывается done он посылает всем работникам сигнал отмены, и ждёт завершениея и диспетчиризирует остатки сообщений, при этом гарантировано если был вызван begin будет вызван end.
И все работники гарантировано освободят ресурсы, у них тоже довольно структурированный вид, и они не подозревают в каком потоке работают.

BFE>Известно как — запомнить указатель и вернуть его.

BFE>Хотите экономить на спичках, пишите в C стиле: без виртуальности и наследований.
Причем тут виртуальность и наследование. Просто в C++ нет единообразия, для разных сущностей всё делается по своему. Так и указатели на члены класса это отдельная песня.
Re[9]: Как такое написать по Сиплюсплюсному?
От: B0FEE664  
Дата: 24.03.25 18:12
Оценка: -1 :)
Здравствуйте, kov_serg, Вы писали:

BFE>> Если применять патерны C на С++, то так и получается.

BFE>>А вот если использовать нормальный патерн, вроде signal-slot или, там std::future / std::async / std::promise, то код будет выглядеть совсем иначе.
_>signal-slot как в qt забабахав предкомпиляцию, что бы весь треш спрятать под ковёр.
Не надо как в Qt. Qt отягощён своим наследием и к использованию совсем не обязателен. На современном C++ патерн signal-slot пишется за пару дней, если не заворачиваться со скоростью.

_>futute/promise никаких гарантий не дают и общаться с рабочим потоком прдётся через свои прослойки. Если можете показать как это элегантно сделать — покажите.

Да, через свои прослойки.

BFE>>Вариант, как вариант, видал я и позабористей. А вот то, что хорош — нет: если GUIWindow умрет раньше Workers, то это "всё", "падаем".

_>нет. Тут как раз при убийстве GUIWindow вызывается done он посылает всем работникам сигнал отмены,...
Из кода не видно, что это делается автоматически (например в деструкторе workers done() вызваться не может (т.е. может, но приведёт к падению)), так что скорее всего руками, а значит можно забыть прописать...

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

Что означает, что работники не могут одновременно работать на несколько окон.

_>Просто в C++ нет единообразия, для разных сущностей всё делается по своему.

Без микроменеджмента эффективности не будет. Где всё единообразно, там медленно.
И каждый день — без права на ошибку...
Re[10]: Как такое написать по Сиплюсплюсному?
От: kov_serg Россия  
Дата: 24.03.25 18:36
Оценка:
Здравствуйте, B0FEE664, Вы писали:

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

BFE>Что означает, что работники не могут одновременно работать на несколько окон.
Обработчики выполняют конкретное действие. Другие окна могут использовать уведомления (Notifications) для отображения моделий происходящих процессов если это необходимо. При этом количество окон не ограничено.
Это не threadpool это именно действие, как вызов функции. Просто это постепенно исполняемые функции. Если её вызываю раньше чем она закончилась, сначала отменяется текущая, и затем запускается новая.
Например обработка поиска, на вход поступает запрос, и по мере исполнения заполняется список, при этом в любой момент можно прервать и выполнить новый запрос.
Если надо кучу параллельных запросов сделать — делается пачка таких функций и каждая их них мапится в свою модель, которая оповещает подпищиков. Главная задача скрыть многопоточность.

_>>Просто в C++ нет единообразия, для разных сущностей всё делается по своему.

BFE>Без микроменеджмента эффективности не будет. Где всё единообразно, там медленно.
Эффективность и скорость это разные понятия. Эффективно это с высоким кпд, а быстро это не обязательно эффективно.
Ничто не мешает на медленном питоне построить вычислительный граф и потом его запулить на gpu. Что выполнит вычиления на порядки быстрее чем на cpu.
Я про другое, должна быть возможность любой сущности дать имя и по этому имени работать с ней. В C++ это не так, вы даже некоторым сущностям имя не можете дать, или хотя бы ссылку. Не говоря о том чо там наплодили вагон и маленькую тележку сущностей для которых немененно мелких нюансов. В результате создаём ненужные сложности, которые затем вынуждены героически преодолевать.
Отредактировано 24.03.2025 18:38 kov_serg . Предыдущая версия .
Re[7]: Как такое написать по Сиплюсплюсному?
От: rg45 СССР  
Дата: 25.03.25 08:45
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>В C было просто достаточно двух указателей и кучи констант и никаких тебе UB


Ну так в чём проблема? Используй только тривиальные standard layout структуры, будет тебе то же самое и в C++
--
Справедливость выше закона. А человечность выше справедливости.
Re[7]: шаблонные виртуальные
От: B0FEE664  
Дата: 27.03.25 15:35
Оценка: -1 :)
Здравствуйте, kov_serg, Вы писали:

BFE>> или шаблонные виртуальные функции, которые в C++ все еще не могут добавить из-за косности мышления

_>А для чего они могут понадобиться? И как это должно выглядеть?

Взглянем на
struct Listner {
    virtual void begin(void *args)=0;
    virtual int  progress(double p)=0;
    virtual void end(void *res)=0;
};


Почему void *args и void *res? Потому, что это стирание типа. С потерей строгости.
Как могло бы быть?
Я считаю, что, например, так:
struct Listner 
{
    template<TArg arg> virtual void begin(TArg arg)=0;
                       virtual int  progress(double p)=0;
    template<TRes res> virtual void end(TRes res)=0;
};


Тогда:
struct Action1 : Listner 
{
   template<TArg arg> virtual void begin(TArg arg)         { assert(false && "unexpected"); throw std::runtime_error("wrong object"); };
   template<>         virtual void begin(Action1Args* arg) { /* some usefull code */ };
                      virtual int  progress(double p)      { /* some usefull code */ };
   template<TRes res> virtual void end(TRes res)           { assert(false && "unexpected"); throw std::runtime_error("wrong object"); };
   template<>         virtual void end(Action1Result* arg) { /* some usefull code */ };
};

или так
struct Action1 : Listner 
{
   virtual void begin(Action1Args* arg) { /* some usefull code */ };
   virtual void begin(Action2Args* arg) { /* some usefull code */ };
   virtual void begin(Action3Args* arg) { /* some usefull code */ };
   // если перегрузка, скажем, для Action4Args* не найдена, то ошибка линковки

   virtual int  progress(double p)      { /* some usefull code */ };

   virtual void end(Action1Result* arg) { /* some usefull code */ };
   virtual void end(Action2Result* arg) { /* some usefull code */ };
   virtual void end(Action3Result* arg) { /* some usefull code */ }; 
   // если перегрузка не найдена, то ошибка линковки
};

Прошу заметить, что сам Listner не шаблонный, а значит указатели на объекты этого типа можно сложить в контейнер типа std::vector<Listner*>

В таком случае у Listner либо динамическая таблица виртуальных функций, либо таблица виртуальных функций формируется во время линковки, а не компиляции. Писатели компиляторов от такого сильно страдают: у них либо шаблон рвётся, либо мозг взрывается, либо это противоречит их философии раздельной компиляции и следовательно не существует в их мире.
Поэтому время от времени, читая чужой код, я сталкиваюсь с той или иной инкарнацией этой идеи, чаще с использованием старателя типа, чем в виде массивов функций. (Никогда не встречал в промышленном коде в виде двойной диспетчеризации, но теоретически это тоже самое и тоже вручную, когда шаблонную функцию расписывают в набор нешаблонных...)

Говорят, что Страуструп пытался что-то замутить с обобщёнными функциями, что связано с шаблонными виртуальными, но как-то мало новостей по этой теме...
И каждый день — без права на ошибку...
Re[8]: шаблонные виртуальные
От: kov_serg Россия  
Дата: 27.03.25 17:04
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>>> или шаблонные виртуальные функции, которые в C++ все еще не могут добавить из-за косности мышления

_>>А для чего они могут понадобиться? И как это должно выглядеть?

BFE>Взглянем на

BFE>
BFE>struct Listner {
BFE>    virtual void begin(void *args)=0;
BFE>    virtual int  progress(double p)=0;
BFE>    virtual void end(void *res)=0;
BFE>};
BFE>


BFE>Почему void *args и void *res? Потому, что это стирание типа. С потерей строгости.

Да это убирание типа за ненадобностью. Это не потеря строгости, а приобретение гибкости. Строгость обеспечивается на другом уровне.

BFE>Как могло бы быть?

BFE>Я считаю, что, например, так:
BFE>
BFE>struct Listner 
BFE>{
BFE>    template<TArg arg> virtual void begin(TArg arg)=0;
BFE>                       virtual int  progress(double p)=0;
BFE>    template<TRes res> virtual void end(TRes res)=0;
BFE>};
BFE>


BFE>Тогда:

BFE>
BFE>struct Action1 : Listner 
BFE>{
BFE>   template<TArg arg> virtual void begin(TArg arg)         { assert(false && "unexpected"); throw std::runtime_error("wrong object"); };
BFE>   template<>         virtual void begin(Action1Args* arg) { /* some usefull code */ };
BFE>                      virtual int  progress(double p)      { /* some usefull code */ };
BFE>   template<TRes res> virtual void end(TRes res)           { assert(false && "unexpected"); throw std::runtime_error("wrong object"); };
BFE>   template<>         virtual void end(Action1Result* arg) { /* some usefull code */ };
BFE>};
BFE>

BFE>или так
BFE>
BFE>struct Action1 : Listner 
BFE>{
BFE>   virtual void begin(Action1Args* arg) { /* some usefull code */ };
BFE>   virtual void begin(Action2Args* arg) { /* some usefull code */ };
BFE>   virtual void begin(Action3Args* arg) { /* some usefull code */ };
BFE>   // если перегрузка, скажем, для Action4Args* не найдена, то ошибка линковки

BFE>   virtual int  progress(double p)      { /* some usefull code */ };

BFE>   virtual void end(Action1Result* arg) { /* some usefull code */ };
BFE>   virtual void end(Action2Result* arg) { /* some usefull code */ };
BFE>   virtual void end(Action3Result* arg) { /* some usefull code */ }; 
BFE>   // если перегрузка не найдена, то ошибка линковки
BFE>};
BFE>

BFE>Прошу заметить, что сам Listner не шаблонный, а значит указатели на объекты этого типа можно сложить в контейнер типа std::vector<Listner*>
На самом деле у ActionN всегда есть вход и выход. Явный тип. При этом задача переходника просто преобразовать типы указателей с наименьшим гемороем. Более того есть некоторые договорённости (контракты) которые
должны соблюдасть обеими сторонами. Компилятор в подобными сущностями не оперирует и будет только мешать при избыточной назойливости.

BFE>В таком случае у Listner либо динамическая таблица виртуальных функций, либо таблица виртуальных функций формируется во время линковки, а не компиляции. Писатели компиляторов от такого сильно страдают: у них либо шаблон рвётся, либо мозг взрывается, либо это противоречит их философии раздельной компиляции и следовательно не существует в их мире.

BFE>Поэтому время от времени, читая чужой код, я сталкиваюсь с той или иной инкарнацией этой идеи, чаще с использованием старателя типа, чем в виде массивов функций. (Никогда не встречал в промышленном коде в виде двойной диспетчеризации, но теоретически это тоже самое и тоже вручную, когда шаблонную функцию расписывают в набор нешаблонных...)

BFE>Говорят, что Страуструп пытался что-то замутить с обобщёнными функциями, что связано с шаблонными виртуальными, но как-то мало новостей по этой теме...


Так это можно реализовать на c++ и виртуальность там вообще не нужна. Будет просто динамическая диспечеризация.
Схематично: Делается вардик шаблонная функция котора получает свой typeinfo (использует его как ключ) сеариализует все свои параметры и ключ в пакет (packet/parcel/string ...) не суть.
Далее делаем мапу где ключ typeinfo метода и заполняем обработчики. Обработчик должен десериализовать параметры и вызвать функцию обработчик.
Но возникает куча засад с приведением типов. Поэтому такой подход будет крайне хреновым. Или придётся приводить ключи и потом типы что крайне накладно и не очевидно и не однозначно. Кучу типов не серализуются в лоб или вообще только с посредниками. В общем сразу нафиг.

Гораздо проще просто сделать диспечеризацию удалённых вызовов без такого гемороя и сериализовать и парсить аргументы для заранее определённого типа сигнатур с уникальными id-никами. Короче некий ограниченный апи.
В самом общем виде обработчик для обычных функций будет такой:
typedef int (*dispatch_fn)(void *ctx,Packet *result,Packet *input);
Re[7]: Как такое написать по Сиплюсплюсному?
От: vopl Россия  
Дата: 28.03.25 04:51
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>если байт это 8бит то в C++ он имеет 257 значений


Можешь разъяснить/продемонстрировать процитированное пожалуйста?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.