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

    оригинал на 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 под функторами немного другое подразумевается. Но я не отговариваю )
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:
                                Страницы: (3) [1] 2 3  все


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0536 ]   [ 15 queries used ]   [ Generated: 6.12.23, 21:11 GMT ]