На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела:
1. Название темы - краткое описание кто/что против кого/чего
2. В первом сообщении - список параметров, по которым идет сравнение.
3. Старайтесь аргументировать свои высказывания. Фразы типа "Венда/Слюникс - ацтой" считаются флудом.
4. Давайте жить дружно и не доводить обсуждение до маразма и личных оскорблений.
Модераторы: Модераторы, Комодераторы
  
> Шаблоны — примеры применения и альтернативы на других языках
    Пролистывая старые темы, наткнулся на пример.

    оригинал на C++ от Qraizer

    ExpandedWrap disabled
      // Шаблон, использующий статический полиморфизм
      template <typename MT_Policy>
      class Foo: public MT_Policy
      {
       /* ... */
       
      public:
       /* ... */
       void DoSomething()
       {
        this->lock();
       
        /* do something */
       
        this->unclock();
       }
      };
       
      // Реализация для однопоточного приложения
      class NoSync
      {
      public:
       void lock()  {}
       void unlock(){}
      };
       
      // Реализация через критические секции
      class CT_Sync
      {
       CRITICAL_SECTION ct;
       
       CT_Sync(const CT_Sync&);
       void operator=(const CT_Sync&);
       
      public:
        CT_Sync()   { InitializeCriticalSection(&ct); }
       ~CT_Sync()   { DeleteCriticalSection    (&ct); }
       
       void lock()  { EnterCriticalSection(&ct); }
       void unlock(){ LeaveCriticalSection(&ct); }
      };
       
      // Реализация через мьютексы
      class Mutex_Sync
      {
       HANDLE hMutex;
       
       Mutex_Sync(const Mutex_Sync&);
       void operator=(const Mutex_Sync&);
       
      public:
        Mutex_Sync(): hMutex(CreateMutex(NULL, FALSE, NULL))
        {
         if (hMutex == NULL) throw std::runtime_error("Can\'t create a mutex");
        }
       ~Mutex_Sync()   { CloseHandle(hMutex); }
       
       void lock()  { WaitForSingleObject(hMutex, INFINITE); }
       void unlock(){ ReleaseMutex(hMutex); }
      };
       
      // Использование
      Foo<CT_Sync>    privateFoo;
      Foo<Mutex_Sync> sharedFoo;
      Foo<NoSync>     upToF_ckFoo;


    Далее IL_Agent привёл вариант на C#, заметив, что унаследоваться от параметра дженерика нельзя.
    вариант на C# от IL_Agent

    ExpandedWrap disabled
      class Foo<Policy> where Policy : ISync
          {
              Policy sync = Activator.CreateInstance<Policy>();
       
              public void DoSomething()
              {
                  sync.Lock();
                  //...
                  sync.Unlock();
              }
          }
       
          interface ISync
          {
              void Lock();
              void Unlock();        
          }
       
          class NoSync:ISync
          {      
              public void Lock() { }
              public void Unlock() { }        
          }
       
          class SomeSync : ISync
          {
              //...
              public SomeSync(){/*...*/ }
              public void Lock() {/*...*/ }
              public void Unlock(){/*...*/ }
          }
       
      //...
      var f1 = new Foo<NoSync>();
      var f2 = new Foo<SomeSync>();


    Код упрощённый — в C# нет RAII, поэтому нужна ещё финализация (например, для политики с критической секцией).

    Также, D_KEY заметил, что в C++ lock/unlock могут быть статическими функциями.
    В оригинальном примере они вызываются через this->, в Java можно вызывать статические функции через this. В C++ тоже?
    Или имелось в виду, что в шаблоне нужно вызывать их без this->, тогда можно будет использовать статические? Или нужен второй шаблон Foo для "статического" вида политик?
    D_KEY, уточни, пожалуйста.

    Решил попробовать реализовать аналог на Ocaml и вот что получилось:
    ExpandedWrap disabled
      module type MT_policy =
        sig
          type t
       
          class handler : object
            val handle : t
            method finalize : unit
          end
       
          val lock   : t -> unit
          val unlock : t -> unit
        end
       
      module Foo (Policy : MT_policy) : sig
        class t : object
          method do_something : unit
          method finalize     : unit
        end
      end = struct
       
        class t = object
          inherit Policy.handler
       
          method do_something =
            Policy.lock handle ;
            print_endline "do something" ;
            Policy.unlock handle
        end
       
      end
       
       
      (* ---------------------------------------------------------------- *)
       
          (* ----------------
             external library
             ---------------- *)
       
       
      module CRITICAL_SECTION : sig
        type t
        val define     : unit -> t
        val initialize : t -> unit
        val delete     : t -> unit
        val enter      : t -> unit
        val leave      : t -> unit
      end = struct
       
        type t = int ref
        let define () = ref 0
       
        let log action sec = Printf.printf "%s SEC %d\n" action !sec
       
        let initialize sec = sec := 42 ; log "INIT" sec
        let delete     sec = log "DELETE" sec ; sec := 0
        let enter      sec = log "ENTER" sec
        let leave      sec = log "LEAVE" sec
       
      end
       
       
      module Mutex : sig
        type t
        val create   : unit -> t
        val wait_for : t -> unit
        val release  : t -> unit
      end = struct
       
        type t = Mx
       
        let create   () = Mx
        let wait_for Mx = print_endline "Waiting for mutex..."
        let release  Mx = print_endline "Release mutex"
       
      end
       
       
      (* ---------------------------------------------------------------- *)
       
       
      module No_sync = struct
       
        type t = unit
       
        class handler = object
          val handle = ()
          method finalize = ()
        end
       
        let lock   () = print_endline "no lock"
        let unlock () = print_endline "no unlock"
       
      end
       
       
      module CT_sync = struct
       
        type t = CRITICAL_SECTION.t
       
        class handler = object
          val handle = CRITICAL_SECTION.define ()
          method finalize = CRITICAL_SECTION.delete handle
        initializer
          CRITICAL_SECTION.initialize handle
        end
       
        let lock   = CRITICAL_SECTION.enter
        let unlock = CRITICAL_SECTION.leave
       
      end
       
       
      module MX_sync = struct
       
        type t = Mutex.t
       
        class handler = object
          val handle = Mutex.create ()
          method finalize = ()
        end
       
        let lock   = Mutex.wait_for
        let unlock = Mutex.release
       
      end
       
       
      (* ---------------------------------------------------------------- *)
       
       
      let new_foo (policy : (module MT_policy)) =
        let module F = Foo (val policy) in
        new F.t
       
      type 'a finalizable =
        < finalize : unit
        ; .. > as 'a
       
      let using : 'a finalizable -> ('a -> unit) -> unit
        = fun res f ->
          f res ;
          res #finalize
       
      let _ =
        using (new_foo (module CT_sync)) @@ fun private_foo ->
        using (new_foo (module MX_sync)) @@ fun shared_foo ->
        using (new_foo (module No_sync)) @@ fun up_to_f_ck_foo ->
       
        print_endline "---- doing something..." ;
       
        private_foo    # do_something ;
        shared_foo     # do_something ;
        up_to_f_ck_foo # do_something ;
       
        print_endline "---- finalizing..."

    Результат запуска:
    ExpandedWrap disabled
      INIT SEC 42
      ---- doing something...
      ENTER SEC 42
      do something
      LEAVE SEC 42
      Waiting for mutex...
      do something
      Release mutex
      no lock
      do something
      no unlock
      ---- finalizing...
      DELETE SEC 42

    https://www.jdoodle.com/ia/Acd

    В Ocaml тоже нет RAII, поэтому нужна явная финализация. В остальном: функции lock/unlock полностью абстрактные, могут быть «статическими», могут — не быть, например, в конкретной политике можно реализовать тип t как класс с методами lock/unlock, которые будут вызываться из функций. Класс Policy.handler можно наследовать, можно — не наследовать.

    Предлагайте свои варианты на других языках (статически типизированных, конечно).
    C# там уже был; Java, наверное, не будет от него отличаться слишком (хотя, вдруг, у кого есть альтернативные идеи).
    Может, тут Swift'ингеры есть? Или Rust'оманы? Или D_KEY вспомнит Scala? =)

    Также было бы интересно увидеть ещё какие-нибудь подобные примеры использования шаблонов.
    (Наверняка, они были в тех многочисленных старых холиварах, но копаться там — занятие такое себе. А тут можно было бы собрать несколько интересных примеров в одной теме)
      Цитата korvin @
      В оригинальном примере они вызываются через this->, в Java можно вызывать статические функции через this. В C++ тоже?

      Да, тоже можно.

      Добавлено
      Цитата korvin @
      Предлагайте свои варианты на других языках (статически типизированных, конечно).

      Тут вся фишка в С++ в том, что политиками ты статически как бы "собираешь" свой тип, но при этом ты ничем не жертвуешь в динамкие. Т.е. как будто ты сам писал все эти классы руками делал.
      Т.е. абстракция остается исключительно в коде, а не в итоговой программе. Не думаю, что в других языках такое есть.
      Может на неделе посмотрю, есть ли что-то такое в расте.
        Откровенно говоря, в моём примере тоже нет финализации, но её легко построить:
        ExpandedWrap disabled
          template <typename MT_Policy>
          class Guard
          {
            MT_Policy& synk;
           
          public:
            explicit Guard(MT_Policy& obj): synk(obj) { synk.lock(); }
            ~Guard() { synk.unlock(); }
          };

        Тогда использование будет чуточку проще, но и гарантированно безопасно:
        ExpandedWrap disabled
          template <typename MT_Policy>
          class Foo: public MT_Policy
          {
            /* ... */
           
          public:
            /* ... */
            void DoSomething()
            {
              Guard<MT_Policy> guardian(*this);
           
              /* do something */
            }
          };

        В общем случае можно защитить не только функцию целиком, но и произвольный кусок кода, достаточно заключить его в синтетический блок {}:
        ExpandedWrap disabled
          void DoSomething()
          {
            /* do something */
           
            {
              Guard<MT_Policy> guardian(*this);
           
              /* do something more */
            }
            /* continue something doing*/
          }
        Сообщение отредактировано: Qraizer -
          Qraizer, зачем там свой Guard? Достаточно удовлетворять BasicLockable и можно юзать std::lock_guard.
          Сообщение отредактировано: D_KEY -
            Тогда и CT_Sync с Mutex_Sync не нужны, ведь есть std::mutex и std::recursive_mutex. Вопрос в той теме стоял несколько по-другому: продемонстрировать возможности шаблонов в части устранения ненужного динамического оверхеда интерфейсов в случае статики. Я не стал менять коней, ибо не для всякой ситуации в std есть готовые патерны.

            Добавлено
            P.S. Ну и чтобы совсем хорошо, следует избавиться от наследования в пользу объекта-агрегата. Наследование там не требуется, а агрегат можно сделать mutable и т.с. позволить работать в константных методах.
              Цитата Qraizer @
              Тогда и CT_Sync с Mutex_Sync не нужны, ведь есть std::mutex и std::recursive_mutex.

              Ну мы же о стратегиях говорим. Можем написать разные классы, в том числе и для отсутствия блокировок. При этом std::lock_guard будет применим :-?
                Блин. Ты не понял. Ну давай за стратегию безопасности кастов целых тогда. Вот я хочу чекать переполнения или не хочу. Или за стратегию транспорта для сетевого протокола. Элементарно TCP vs UDP. У тебя есть что-то в std для этого?
                Ещё раз. В общем случае нужно рассчитывать, что стратегии придётся писать фуллстэк. На всех уровнях, распространяя их сверху донизу иерархии или наоборот. И шаблоны на это способны. Тут да, можно применить std::, я просто не стал менять коней на полпути.
                Сообщение отредактировано: Qraizer -
                  Цитата Qraizer @
                  Откровенно говоря, в моём примере тоже нет финализации

                  Хм, а я думал деструктора достаточно:

                  ExpandedWrap disabled
                    public:
                      CT_Sync()   { InitializeCriticalSection(&ct); }
                     ~CT_Sync()   { DeleteCriticalSection    (&ct); }
                    Это если мы за собственно объект. А как быть с блокировкой на нём? Нужно обеспечить корректное освобождение даже при исключениях, что и делает RAII. В исходном коде RAII существовала для объекта синхронизации, но не блокировки на нём.
                    Хотя, это зависит от того, что ты понимаешь под финализацией, конечно.
                    Сообщение отредактировано: Qraizer -
                      Я тем временем сделал альтернативную Ocaml-версию, без объектов, чисто на функторах, и заодно немного по-другому сделал финализацию:

                      ExpandedWrap disabled
                        module type MT_policy =
                          sig
                            val lock   : unit -> unit
                            val unlock : unit -> unit
                          end
                         
                         
                        module Foo (Policy : MT_policy) = struct
                         
                          let do_something () =
                            Policy.lock () ;
                            print_endline "Doing something..." ;
                            Policy.unlock ()
                         
                        end
                         
                         
                        (* -------- EXT LIB -------- *)
                         
                         
                        module CRITICAL_SECTION :
                          sig
                            type t
                            val value      : unit -> t
                            val initialize : t -> unit
                            val enter      : t -> unit
                            val leave      : t -> unit
                            val delete     : t -> unit
                          end =
                        struct
                          
                          type t = int ref
                          
                          let value () = ref 0
                          
                          let initialize sec =
                            let sid = Random.int 100 in
                            Printf.printf "---- INIT SEC %d\n" sid ;
                            sec := sid
                          
                          let enter sec =
                            Printf.printf "ENTER SEC %d\n" !sec
                          
                          let leave sec =
                            Printf.printf "LEAVE SEC %d\n" !sec
                          
                          let delete sec =
                            Printf.printf "---- DEL SEC %d\n" !sec ;
                            sec := 0
                         
                        end
                         
                         
                        module Mutex :
                          sig
                            type t
                            val construct : unit -> t
                            val acquire   : t -> unit
                            val release   : t -> unit
                            val destruct  : t -> unit
                          end =
                        struct
                         
                          type t = int ref
                          
                          let construct () =
                            let mid = 100 + Random.int 100 in
                            Printf.printf "---- CONSTR MUX %d\n" mid ;
                            ref mid
                          
                          let acquire mux =
                            Printf.printf "ACQ MUX %d\n" !mux
                          
                          let release mux =
                            Printf.printf "REL MUX %d\n" !mux
                          
                          let destruct mux =
                            Printf.printf "---- DESTR MUX %d\n" !mux ;
                            mux := 0
                         
                        end
                         
                         
                        (* -------------------------------- *)
                         
                         
                        module No_sync : MT_policy = struct
                         
                          let lock     () = ()
                          let unlock   () = ()
                         
                        end
                         
                         
                        module type REGION =
                          sig
                            val finalize : ('a -> unit) -> 'a -> unit
                          end
                         
                         
                        module CT_sync (R : REGION) : MT_policy = struct
                         
                          let ct = CRITICAL_SECTION.value ()
                         
                          let _ = CRITICAL_SECTION.initialize ct
                          let _ = R.finalize CRITICAL_SECTION.delete ct
                          
                          let lock   () = CRITICAL_SECTION.enter ct
                          let unlock () = CRITICAL_SECTION.leave ct
                         
                        end
                         
                         
                        module MX_sync (R : REGION) : MT_policy = struct
                         
                          let mx = Mutex.construct ()
                          let _  = R.finalize Mutex.destruct mx
                          
                          let lock   () = Mutex.acquire mx
                          let unlock () = Mutex.release mx
                         
                        end
                         
                         
                        (* -------------------------------- *)
                         
                         
                        module Region () :
                          sig
                            val finalize : ('a -> unit) -> 'a -> unit
                            val exit     : unit -> unit
                          end =
                        struct
                         
                          let finalizers = ref []
                          
                          let finalize f v =
                            finalizers := (fun () -> f v) :: !finalizers
                          
                          let rec exit () =
                            match !finalizers with
                            | []    -> ()
                            | f::fs ->
                              begin
                                f () ;
                                finalizers := fs ;
                                exit ()
                              end
                         
                        end
                         
                         
                        let _ =
                          Random.self_init () ;
                          let module REGION = Region () in
                          
                          let module F1 = Foo (CT_sync (REGION)) in
                          let module F2 = Foo (MX_sync (REGION)) in
                          let module F3 = Foo (No_sync) in
                          let module F4 = Foo (CT_sync (REGION)) in
                          let module F5 = Foo (MX_sync (REGION)) in
                          
                          F1.do_something () ;
                          F2.do_something () ;
                          F3.do_something () ;
                          F4.do_something () ;
                          F5.do_something () ;
                          
                          REGION.exit ()

                      =>
                      ExpandedWrap disabled
                        ---- INIT SEC 35
                        ---- CONSTR MUX 181
                        ---- INIT SEC 0
                        ---- CONSTR MUX 168
                        ENTER SEC 35
                        Doing something...
                        LEAVE SEC 35
                        ACQ MUX 181
                        Doing something...
                        REL MUX 181
                        Doing something...
                        ENTER SEC 0
                        Doing something...
                        LEAVE SEC 0
                        ACQ MUX 168
                        Doing something...
                        REL MUX 168
                        ---- DESTR MUX 168
                        ---- DEL SEC 0
                        ---- DESTR MUX 181
                        ---- DEL SEC 35


                      https://godbolt.org/z/K4b6xGG15
                        Хм. Формально ничто не запрещает на функторах переписать... D_KEY, как думаешь, стоит ли?
                        Сообщение отредактировано: Qraizer -
                          Цитата Qraizer @
                          но не блокировки на нём.

                          А, понял. Я про lock/unlock в doSomething и не думал, как-то второстепенным уже казалось ))
                            В целом да, это второстепенное. Но с точки зрения отказоустойчивости кода – ни разу не второстепенное. Надёжность моего кода не должна зависеть от надёжности чьего-то ещё. Без RAII можно заморочиться чеком всевозможных нюансов между lock() и unlock() внутри DoSomething(), и если заморочился и не набажил, то возьми с полки пирожок. Кто как, я вот предпочитаю пирожок в руках сразу, а не тянуться за ним до полки.
                              В Ocaml можно вот так сделать, но это только в теле функции, аналог try-with-resources в Java и прочих:

                              ExpandedWrap disabled
                                type 'a resource =
                                  { acquire : unit -> 'a
                                  ; release : 'a -> unit }
                                 
                                 
                                let ( let& ) : 'a resource -> ('a -> 'b) -> 'b
                                  = fun r f ->
                                    let h = r.acquire () in
                                    Fun.protect ~finally:(fun () -> r.release h) begin fun () ->
                                      f h
                                    end
                                 
                                 
                                module type MT_policy =
                                  sig
                                    val lock   : unit -> unit
                                    val unlock : unit -> unit
                                  end
                                 
                                 
                                module Foo (Policy : MT_policy) = struct
                                 
                                  let lock () =
                                    { acquire = Policy.lock
                                    ; release = Policy.unlock }
                                 
                                  let do_something () =
                                    let& _ = lock () in
                                    print_endline "Doing something..."
                                 
                                end
                                Цитата Qraizer @
                                Формально ничто не запрещает на функторах переписать...

                                В Ocaml под функторами немного другое подразумевается. Но я не отговариваю )
                                  Цитата Qraizer @
                                  Хм. Формально ничто не запрещает на функторах переписать... D_KEY, как думаешь, стоит ли?

                                  А зачем? :)

                                  Добавлено
                                  Цитата korvin @
                                  В Ocaml можно вот так сделать, но это только в теле функции, аналог try-with-resources в Java и прочих

                                  Да, с плюсами разница как раз в том, что только в функции это работает. В С++ же это так же будет работать и для полей и автоматически вызываться при разрушении объекта.
                                    Цитата korvin @
                                    void operator=(const CT_Sync&);

                                    Цитата korvin @
                                    void operator=(const Mutex_Sync&);

                                    А что, так можно перегружать?

                                    Обычно же пишут что-то в виде:

                                    ExpandedWrap disabled
                                      Type& operator=(const Type& t);
                                      Раст.
                                      ExpandedWrap disabled
                                        trait Policy {
                                            fn new() -> Self;
                                            fn lock(&mut self);
                                            fn unlock(&mut self);
                                        }
                                         
                                        struct LockGuard<'a, P: Policy> {
                                            p: &'a mut P,
                                        }
                                         
                                        impl<'a, P: Policy> LockGuard<'a, P> {
                                            fn new(p: &'a mut P) -> Self {
                                                p.lock();
                                                Self { p }
                                            }
                                        }
                                         
                                        impl<'a, P: Policy> Drop for LockGuard<'a, P> {
                                            fn drop(&mut self) {
                                                self.p.unlock();
                                            }
                                        }
                                         
                                        struct Foo<P: Policy> {
                                            p: P,
                                        }
                                         
                                        impl<P: Policy> Foo<P> {
                                            fn new() -> Foo<P> {
                                                Foo { p: Policy::new() }
                                            }
                                         
                                            fn do_smth(&mut self) {
                                                let lock = LockGuard::new(&mut self.p);
                                                // Do something
                                                println!("Hello!");
                                            }
                                        }
                                         
                                        struct NoLock {}
                                         
                                        impl Policy for NoLock {
                                            fn new() -> Self {
                                                Self {}
                                            }
                                         
                                            fn lock(&mut self) {}
                                            fn unlock(&mut self) {}
                                        }


                                      Добавлено
                                      Из удобного - спева написал версию с ручным lock-unlock, потом добавил LockGuard, заменил вызов lock на создание объекта LockGuard и компилятор сразу выдал ошибку - дескать, unlock руками вызывать не можешь, т.к. объект отдали. BC всё-таки удобная вещь :)
                                        Цитата D_KEY @
                                        Да, с плюсами разница как раз в том, что только в функции это работает. В С++ же это так же будет работать и для полей и автоматически вызываться при разрушении объекта.

                                        Спасибо, Кэп ))
                                          Можно так:

                                          ExpandedWrap disabled
                                            module Region :
                                              sig
                                                type region
                                                val ( #: )   : region -> ('a -> unit) -> 'a -> unit
                                                val ( let& ) : (region -> 'a) -> ('a -> 'b) -> 'b
                                              end =
                                            struct
                                             
                                              type finalizer = unit -> unit
                                             
                                              type region = finalizer list ref
                                              
                                              let make () = ref []
                                              
                                              let ( #: ) r f v =
                                                r := (fun () -> f v) :: !r
                                             
                                              let rec exit r =
                                                match !r with
                                                | []    -> ()
                                                | f::fs ->
                                                  begin
                                                    f () ;
                                                    r := fs ;
                                                    exit r
                                                  end
                                              
                                              let ( let& ) : (region -> 'a) -> ('a -> 'b) -> 'b
                                                = fun constr proc ->
                                                  let region = make () in
                                                  Fun.protect
                                                    ~finally:(fun () -> exit region)
                                                    begin fun () ->
                                                      let res = constr region in
                                                      proc res
                                                    end
                                             
                                            end
                                             
                                            open Region
                                             
                                             
                                            module File :
                                              sig
                                                type t
                                                val open_rw    : string -> region -> t
                                                val read_line  : t -> string option
                                                val write_line : t -> string -> unit
                                              end =
                                            struct
                                             
                                              type t =
                                                {         path : string
                                                ; mutable data : string list }
                                              
                                              let rec open_rw path region =
                                                Printf.printf "++++ OPEN %s\n" path ;
                                                let f = { path ; data = ["foo";"bar";"gee";"qux"] } in
                                                region #: close f ;
                                                f
                                              
                                              and close f =
                                                f.data <- [] ;
                                                Printf.printf "---- CLOSE %s\n" f.path
                                              
                                              let read_line f =
                                                match f.data with
                                                | [] ->
                                                  begin
                                                    Printf.printf "EOF\n" ;
                                                    None
                                                  end
                                                | line::rest ->
                                                  begin
                                                    f.data <- rest ;
                                                    Printf.printf "READ %s FROM %s\n" line f.path ;
                                                    Some line
                                                  end
                                              
                                              let write_line f line =
                                                Printf.printf "WRITE %s TO %s\n" line f.path ;
                                                f.data <- List.append f.data [line]
                                             
                                            end
                                             
                                             
                                            module SQL_DB :
                                              sig
                                                type t
                                                val connect : string -> region -> t
                                                val select  : t -> string list
                                                val insert  : t -> string -> unit
                                              end =
                                            struct
                                             
                                              type t =
                                                {         table : string
                                                ; mutable rows  : string list }
                                              
                                              let rec connect table region =
                                                Printf.printf "++++ CONNECT TO %s\n" table ;
                                                let c = { table ; rows = ["foo";"bar"] } in
                                                region #: disconnect c ;
                                                c
                                              
                                              and disconnect conn =
                                                conn.rows <- [] ;
                                                Printf.printf "---- DISCONNECT FROM %s\n" conn.table
                                              
                                              let select conn =
                                                Printf.printf "SELECT * FROM %s\n" conn.table ;
                                                conn.rows
                                              
                                              let insert conn value =
                                                Printf.printf "INSERT INTO %s VALUE \"%s\"\n" conn.table value ;
                                                conn.rows <- value :: conn.rows
                                              
                                            end
                                                
                                             
                                            module Transfer :
                                              sig
                                                type t
                                                val make     : db:string -> file:string -> region -> t
                                                val upload   : t -> unit
                                                val download : t -> unit
                                              end =
                                            struct
                                             
                                              type t =
                                                { db : SQL_DB.t
                                                ; fd : File.t }
                                             
                                              let make ~db:conn_str ~file:path region =
                                                let db = SQL_DB.connect conn_str region in
                                                let fd = File.open_rw path region in
                                                { db : SQL_DB.t
                                                ; fd : File.t }
                                              
                                              let upload { db ; fd } =
                                                let rec loop () =
                                                  match File.read_line fd with
                                                  | None -> ()
                                                  | Some line ->
                                                    begin
                                                      SQL_DB.insert db line ;
                                                      loop ()
                                                    end
                                                in loop ()
                                             
                                              let download { db ; fd } =
                                                let lines = SQL_DB.select db in
                                                List.iter (File.write_line fd) lines
                                             
                                            end
                                             
                                             
                                            let _ =
                                              ( let& users = Transfer.make ~db:"users" ~file:"/tmp/users" in
                                                Transfer.upload   users ;
                                                Transfer.download users );
                                              
                                              print_endline "--------------------------------" ;
                                             
                                              ( let& posts = Transfer.make ~db:"posts" ~file:"/tmp/posts" in
                                                Transfer.download posts ;
                                                Transfer.upload   posts );
                                              
                                              print_endline "--------------------------------" ;
                                             
                                              ( let& users = Transfer.make ~db:"users" ~file:"/tmp/users" in
                                                let& posts = Transfer.make ~db:"posts" ~file:"/tmp/posts" in
                                                Transfer.download users ;
                                                Transfer.download posts )

                                          https://godbolt.org/z/eb8e44av5
                                          ExpandedWrap disabled
                                            ++++ CONNECT TO users
                                            ++++ OPEN /tmp/users
                                            READ foo FROM /tmp/users
                                            INSERT INTO users VALUE "foo"
                                            READ bar FROM /tmp/users
                                            INSERT INTO users VALUE "bar"
                                            READ gee FROM /tmp/users
                                            INSERT INTO users VALUE "gee"
                                            READ qux FROM /tmp/users
                                            INSERT INTO users VALUE "qux"
                                            EOF
                                            SELECT * FROM users
                                            WRITE qux TO /tmp/users
                                            WRITE gee TO /tmp/users
                                            WRITE bar TO /tmp/users
                                            WRITE foo TO /tmp/users
                                            WRITE foo TO /tmp/users
                                            WRITE bar TO /tmp/users
                                            ---- CLOSE /tmp/users
                                            ---- DISCONNECT FROM users
                                            --------------------------------
                                            ++++ CONNECT TO posts
                                            ++++ OPEN /tmp/posts
                                            SELECT * FROM posts
                                            WRITE foo TO /tmp/posts
                                            WRITE bar TO /tmp/posts
                                            READ foo FROM /tmp/posts
                                            INSERT INTO posts VALUE "foo"
                                            READ bar FROM /tmp/posts
                                            INSERT INTO posts VALUE "bar"
                                            READ gee FROM /tmp/posts
                                            INSERT INTO posts VALUE "gee"
                                            READ qux FROM /tmp/posts
                                            INSERT INTO posts VALUE "qux"
                                            READ foo FROM /tmp/posts
                                            INSERT INTO posts VALUE "foo"
                                            READ bar FROM /tmp/posts
                                            INSERT INTO posts VALUE "bar"
                                            EOF
                                            ---- CLOSE /tmp/posts
                                            ---- DISCONNECT FROM posts
                                            --------------------------------
                                            ++++ CONNECT TO users
                                            ++++ OPEN /tmp/users
                                            ++++ CONNECT TO posts
                                            ++++ OPEN /tmp/posts
                                            SELECT * FROM users
                                            WRITE foo TO /tmp/users
                                            WRITE bar TO /tmp/users
                                            SELECT * FROM posts
                                            WRITE foo TO /tmp/posts
                                            WRITE bar TO /tmp/posts
                                            ---- CLOSE /tmp/posts
                                            ---- DISCONNECT FROM posts
                                            ---- CLOSE /tmp/users
                                            ---- DISCONNECT FROM users
                                            Цитата OpenGL @
                                            ExpandedWrap disabled
                                              struct Foo<P: Policy> {
                                                  p: P,
                                              }

                                            В оригинальном примере в Foo не было поля p =/
                                            С полем-то каждый может :D
                                            Сообщение отредактировано: korvin -
                                              Цитата korvin @

                                              В оригинальном примере в Foo не было поля p =/

                                              Это да. Но с другой стороны - чем оно мешает? На sizeof класса это не влияет, так что аналог empty base optimization (или как оно там в плюсах зовётся) тут применяется
                                                Цитата Majestio @
                                                Цитата korvin @
                                                void operator=(const CT_Sync&);

                                                Цитата korvin @
                                                void operator=(const Mutex_Sync&);

                                                А что, так можно перегружать?

                                                Обычно же пишут что-то в виде:

                                                ExpandedWrap disabled
                                                  Type& operator=(const Type& t);

                                                Это не обычная перегрузка. Это перегрузка с целью запрета генерировать эти спец.методы компилятором. Цель в том, чтобы запретить копирования и присваивания. Если их просто никак не объявить, компилятор способен будет сгенерировать их сам, если же они объявлены, то генерировать он уже ничего не будет. Но т.к. я их не определил, то при попытке скопировать или присвоить будет ошибка линковки. Т.е. цель запрета на эти операции достигнута, пусть и несколько странным способом. Т.к. я не планирую их использовать, то и прототип может быть любым, Стандарт не запрещает возвращать что ни попадя. Конечно, для обычной перегрузки так писать не стоит, кроме очень специфичных случаев.
                                                В новых Стандартах для такого запрета есть более правильные методы, но на момент написания того кода их ещё не было, или поддерживающие новые фишки Стандарта компиляторы мне не были доступны.
                                                  Цитата Qraizer @
                                                  Это не обычная перегрузка. Это перегрузка с целью запрета генерировать эти спец.методы компилятором. Цель в том, чтобы запретить копирования и присваивания. Если их просто никак не объявить, компилятор способен будет сгенерировать их сам, если же они объявлены, то генерировать он уже ничего не будет. Но т.к. я их не определил, то при попытке скопировать или присвоить будет ошибка линковки. Т.е. цель запрета на эти операции достигнута, путь и несколько странным способом. Т.к. я не планирую их использовать, то и прототип может быть любым, Стандарт не запрещает возвращать что ни попадя. Конечно, для обычной перегрузки так писать не стоит, кроме очень специфичных случаев.
                                                  В новых Стандартах для такого запрета есть более правильные методы, но на момент написания того кода их ещё не было, или поддерживающие новые фишки Стандарта компиляторы мне не были доступны.

                                                  :good:

                                                  Что-то типа X& operator=(const X&) = delete; ?
                                                    Угу. ;)
                                                      Цитата D_KEY @
                                                      Тут вся фишка в С++ в том, что политиками ты статически как бы "собираешь" свой тип, но при этом ты ничем не жертвуешь в динамкие. Т.е. как будто ты сам писал все эти классы руками делал.
                                                      Т.е. абстракция остается исключительно в коде, а не в итоговой программе. Не думаю, что в других языках такое есть.


                                                      В C# варианте, если IPolicy будут реализовывать структуры, а не классы, как в примере, то в ран тайм тоже не будет накладных расходов.
                                                        Привет)
                                                        Справедливости ради, пример на C# был написан так, чтобы внешне быть похожим на плюсовый. На самом деле практической ценности в нём нет, так писать не стоит, дженерики тут не нужны. Достаточно передать в конструктор класса Foo нужный экземпляр ISync.
                                                        И да, что-то "собрать" при компиляции можно с помощью кодогенерации (Roslyn, annotation processing, KSP и т.д), но тут это из пушки по воробьям)
                                                          Цитата IL_Agent @
                                                          Достаточно передать в конструктор класса Foo нужный экземпляр ISync.


                                                          Вот как раз в этом случае у тебя будут потери на косвенный вызов метода через интерфейсную ссылку. Если это не парит, то и прекрасно. А во в случаях когда каждая микросекунда важна - дженерики - наше всё (в .NET)
                                                            Цитата jack128 @
                                                            Вот как раз в этом случае у тебя будут потери на косвенный вызов метода через интерфейсную ссылку.

                                                            Хочешь сказать, что вызов метода конкретного класса быстрее, чем вызов того же метода, но через интерфейс? Есть пруфы?
                                                              Для вызова метода интерфейса ж нужно сначала извлечь адрес метода из VMT и только потом сделать call, но тут разница микроскопическая. А вот что реально важно, так это inline. Если у нас в коде есть только интерфейс, то компилятор(JIT в случае .NET) не в курсе, что именно за метод должен вызваться и честно делает call <адрес метода> , вот если у нас есть структура мы вызваем myStruct.MyMethod(args) то jit сможет заинлайнить MyMethod со всеми вытекающими.

                                                              В .NET классический пример всего этого - это generic math в .NET 6 и ниже. Только после .NET7 таких примеров хрен нагуглишь :-D . А самому писать лень, сорри.
                                                                ExpandedWrap disabled
                                                                  #include <chrono>
                                                                  #include <iostream>
                                                                  #include <thread>
                                                                  #include <utility>
                                                                   
                                                                  /* динамически полиморфная иерархия */
                                                                  struct DynaPolyBase                                                                     // интерфейс
                                                                  {
                                                                    virtual void doIt() const = 0;
                                                                  };
                                                                  // реализации
                                                                  struct DynaPolyDerived1 : DynaPolyBase
                                                                  {
                                                                    virtual void doIt() const {};
                                                                  };
                                                                  struct DynaPolyDerived2 : DynaPolyBase
                                                                  {
                                                                    virtual void doIt() const {};
                                                                  };
                                                                  struct DynaPolyDerived3 : DynaPolyDerived1
                                                                  {
                                                                    virtual void doIt() const {};
                                                                  };
                                                                   
                                                                  /* статически полиморфная иерархия */
                                                                  // реализации
                                                                  struct StatPolyDerived1
                                                                  {
                                                                    void doIt() const {};
                                                                  };
                                                                  struct StatPolyDerived2
                                                                  {
                                                                    void doIt() const {};
                                                                  };
                                                                  struct StatPolyDerived3
                                                                  {
                                                                    void doIt() const {};
                                                                  };
                                                                  template <typename Class> requires ( requires { std::declval<Class>().doIt(); } )       // интерфейс
                                                                  struct StatPolyBase : Class
                                                                  {
                                                                  };
                                                                   
                                                                  /* простой тестер */
                                                                  template <typename C, auto Count = 1000u * 1000 * 1000>
                                                                  void doIt(const C& c)
                                                                  {
                                                                    for (decltype(Count) i = 0; i < Count; ++i) c.doIt();
                                                                  }
                                                                   
                                                                  int main()
                                                                  {
                                                                    auto start = std::chrono::high_resolution_clock::now();
                                                                    StatPolyBase<StatPolyDerived2> item1;
                                                                    DynaPolyDerived2               item2;
                                                                   
                                                                    doIt(item1);                                                          // чек статики
                                                                   
                                                                    auto diff  = std::chrono::high_resolution_clock::now() - start;
                                                                   
                                                                    std::cout << diff << std::endl;
                                                                    start = std::chrono::high_resolution_clock::now();
                                                                    doIt(item2);                                                          // чек динамики
                                                                    diff  = std::chrono::high_resolution_clock::now() - start;
                                                                    std::cout << diff << std::endl;
                                                                  }
                                                                Не знаю, как у вас, у нас вот так:
                                                                ExpandedWrap disabled
                                                                  200ns
                                                                  610564600ns
                                                                  Qraizer, что-то у тя какая-то жуть :-?
                                                                  Я попробовал твой пример прогнать в https://www.onlinegdb.com, выбрал там стандарт С++20, чет он заругался на твой код в отдельных местах. Ну не суть, я подредактировал.
                                                                  Посмотри, я ничего в логике не поломал?

                                                                  ExpandedWrap disabled
                                                                    #include <chrono>
                                                                    #include <iostream>
                                                                    #include <thread>
                                                                    #include <utility>
                                                                     
                                                                    /* динамически полиморфная иерархия */
                                                                    struct DynaPolyBase                                                                     // интерфейс
                                                                    {
                                                                      virtual void doIt() const = 0;
                                                                    };
                                                                    // реализации
                                                                    struct DynaPolyDerived1 : DynaPolyBase
                                                                    {
                                                                      void doIt() const override {};
                                                                    };
                                                                    struct DynaPolyDerived2 : DynaPolyBase
                                                                    {
                                                                      void doIt() const override {};
                                                                    };
                                                                    struct DynaPolyDerived3 : DynaPolyDerived1
                                                                    {
                                                                      void doIt() const override {};
                                                                    };
                                                                     
                                                                    /* статически полиморфная иерархия */
                                                                    // реализации
                                                                    struct StatPolyDerived1
                                                                    {
                                                                      void doIt() const {};
                                                                    };
                                                                    struct StatPolyDerived2
                                                                    {
                                                                      void doIt() const {};
                                                                    };
                                                                    struct StatPolyDerived3
                                                                    {
                                                                      void doIt() const {};
                                                                    };
                                                                    template <typename Class>       // интерфейс
                                                                    struct StatPolyBase : Class
                                                                    {
                                                                    };
                                                                     
                                                                    /* простой тестер */
                                                                    template <typename C, auto Count = 1000u * 1000 * 1000>
                                                                    void doIt(const C& c)
                                                                    {
                                                                      for (auto i = 0; i < Count; ++i) c.doIt();
                                                                    }
                                                                     
                                                                    int main()
                                                                    {
                                                                      
                                                                      StatPolyBase<StatPolyDerived2> item1;
                                                                      DynaPolyDerived2               item2;
                                                                     
                                                                      auto start = std::chrono::high_resolution_clock::now();
                                                                      doIt(item1);                                                          // чек статики
                                                                      auto diff  = std::chrono::high_resolution_clock::now() - start;
                                                                      std::cout << diff.count() << std::endl;
                                                                      
                                                                      start = std::chrono::high_resolution_clock::now();
                                                                      doIt(item2);                                                          // чек динамики
                                                                      diff  = std::chrono::high_resolution_clock::now() - start;
                                                                      std::cout << diff.count() << std::endl;
                                                                    }

                                                                  И вот какие я выводы получил после трех запусков:

                                                                  ExpandedWrap disabled
                                                                    2908372641
                                                                    2742338534

                                                                  ExpandedWrap disabled
                                                                    1867473111
                                                                    2083640450

                                                                  ExpandedWrap disabled
                                                                    1968410219
                                                                    2046030004

                                                                  А в твоем примере вообще какая-то лютая разница :-?
                                                                    Цитата Majestio @
                                                                    Я попробовал твой пример прогнать в https://www.onlinegdb.com, выбрал там стандарт С++20, чет он заругался на твой код в отдельных местах. Ну не суть, я подредактировал.
                                                                    В каких? Концепты не понял? С++20 в полной мере могут не все поддерживать. Та ну и хрен с ними, статика и на утиных работать будет, ей явно объявленные интерфейсы необязательны.
                                                                    Но ты прав, я сутрировал конечно. Намерено, причём. Включи оптимизацию и познай дзен inline, как говорится. ;)
                                                                    Твой пример ближе к истине, но тут такое дело... в общем, предсказатель переходов у современных процессоров всё равно не даст нормально потестить разницу в производительности между статически и динамически связанными вызовами. Надо как-то рандомизировать адресаты, чтобы у предсказателя побольше промахов было, иначе даже косвенные вызовы будут связаны статически, но в кэше процессора. И если для динамики это раз плюнуть, то вот для статики...
                                                                    Но это полбеды. Главная беда – это непонятно, а вообще нужно ли этот самый предсказатель нейтрализовывать. Как бы, синтетика синтетикой, но не до такой же степени, когда код сознательно пессимизируется.

                                                                    Добавлено
                                                                    С горем пополам у меня вышло
                                                                    ExpandedWrap disabled
                                                                      543405900ns
                                                                      578759500ns
                                                                    Это я уменьшил количество вызовов в 10 раз. Вот вам воочию работа предсказателя: более чем 10 кратный штраф за сбросы конвейера.

                                                                    Добавлено
                                                                    P.S. Ну не совсем, всё-таки. Код теперь выглядит примерно вот так:
                                                                    ExpandedWrap disabled
                                                                      template <typename C>
                                                                      auto doIt(const C& c)
                                                                      {
                                                                        auto start = std::chrono::high_resolution_clock::now();
                                                                       
                                                                        c.doIt();
                                                                        return std::chrono::high_resolution_clock::now() - start;
                                                                      }
                                                                      /* ... */
                                                                        StatPolyBase<StatPolyDerived1> itemS1;
                                                                        StatPolyBase<StatPolyDerived2> itemS2;
                                                                        StatPolyBase<StatPolyDerived3> itemS3;
                                                                        DynaPolyDerived1              *itemD1 = new DynaPolyDerived1;
                                                                        DynaPolyDerived2              *itemD2 = new DynaPolyDerived2;
                                                                        DynaPolyDerived3              *itemD3 = new DynaPolyDerived3;
                                                                        std::array<DynaPolyBase*, 3>   items = { itemD1, itemD2, itemD3 };
                                                                       
                                                                        std::random_device              rd;
                                                                        std::mt19937                    gen(rd());
                                                                        std::uniform_int_distribution<> distrib(0, 2);
                                                                       
                                                                        decltype(doIt(itemS1)) time = decltype(time)::zero();
                                                                       
                                                                        // статика
                                                                        for (int i = 0; i < 10000000; ++i)
                                                                          switch (distrib(gen))
                                                                          {
                                                                            case 0: time += doIt(itemS1); break;
                                                                            case 1: time += doIt(itemS2); break;
                                                                            case 2: time += doIt(itemS3); break;
                                                                          }
                                                                      /* ... */
                                                                        // динамика
                                                                        for (int i = 0; i < 10000000; ++i)
                                                                          time += doIt(*items[distrib(gen)]);
                                                                        delete itemD1;
                                                                        delete itemD2;
                                                                        delete itemD3;
                                                                    Т.е. в эти 10 крат входят также вызовы рандом-генератора и службы времени. Так что не всё таки так уж и плохо без предсказателя.

                                                                    Добавлено
                                                                    Если же подойти к этому ну оооочень аккуратно, то:
                                                                    ExpandedWrap disabled
                                                                      547698900ns
                                                                      833802200ns
                                                                    ExpandedWrap disabled
                                                                      700369200ns
                                                                      1015749800ns
                                                                    ExpandedWrap disabled
                                                                      664954600ns
                                                                      962311400ns
                                                                    Тут учитывается оверхед самого вызова std::chrono::high_resolution_clock::now(), усреднённый по миллиарду вызовов. И циклы снова увеличены до миллиарда.
                                                                    Но надо понимать, что если у вас нет аппаратной защиты от всяких там Spectre и вместо стоят программные патчи, то эта синтетика будет показывать попугаев вместо наносекунд.
                                                                    Сообщение отредактировано: Qraizer -
                                                                      Ну да, такое ближе к истине. А то я уже распереживался за динамику, чуть чяем не облился :lol:
                                                                      1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
                                                                      0 пользователей:


                                                                      Рейтинг@Mail.ru
                                                                      [ Script execution time: 0,1296 ]   [ 14 queries used ]   [ Generated: 16.06.25, 04:24 GMT ]