
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[216.73.216.75] |
![]() |
|
Страницы: (3) 1 [2] 3 все ( Перейти к последнему сообщению ) |
Сообщ.
#16
,
|
|
|
Цитата Qraizer @ Хм. Формально ничто не запрещает на функторах переписать... D_KEY, как думаешь, стоит ли? А зачем? ![]() Добавлено Цитата korvin @ В Ocaml можно вот так сделать, но это только в теле функции, аналог try-with-resources в Java и прочих Да, с плюсами разница как раз в том, что только в функции это работает. В С++ же это так же будет работать и для полей и автоматически вызываться при разрушении объекта. |
Сообщ.
#17
,
|
|
|
![]() |
Сообщ.
#18
,
|
|
Раст.
![]() ![]() 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 всё-таки удобная вещь ![]() |
![]() |
Сообщ.
#19
,
|
|
Цитата D_KEY @ Да, с плюсами разница как раз в том, что только в функции это работает. В С++ же это так же будет работать и для полей и автоматически вызываться при разрушении объекта. Спасибо, Кэп )) |
![]() |
Сообщ.
#20
,
|
|
Можно так:
![]() ![]() 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 ![]() ![]() ++++ 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 |
![]() |
Сообщ.
#21
,
|
|
Цитата OpenGL @ ![]() ![]() struct Foo<P: Policy> { p: P, } В оригинальном примере в Foo не было поля p =/ С полем-то каждый может ![]() |
![]() |
Сообщ.
#22
,
|
|
Цитата korvin @ В оригинальном примере в Foo не было поля p =/ Это да. Но с другой стороны - чем оно мешает? На sizeof класса это не влияет, так что аналог empty base optimization (или как оно там в плюсах зовётся) тут применяется |
![]() |
Сообщ.
#23
,
|
|
Цитата Majestio @ Это не обычная перегрузка. Это перегрузка с целью запрета генерировать эти спец.методы компилятором. Цель в том, чтобы запретить копирования и присваивания. Если их просто никак не объявить, компилятор способен будет сгенерировать их сам, если же они объявлены, то генерировать он уже ничего не будет. Но т.к. я их не определил, то при попытке скопировать или присвоить будет ошибка линковки. Т.е. цель запрета на эти операции достигнута, пусть и несколько странным способом. Т.к. я не планирую их использовать, то и прототип может быть любым, Стандарт не запрещает возвращать что ни попадя. Конечно, для обычной перегрузки так писать не стоит, кроме очень специфичных случаев. В новых Стандартах для такого запрета есть более правильные методы, но на момент написания того кода их ещё не было, или поддерживающие новые фишки Стандарта компиляторы мне не были доступны. |
Сообщ.
#24
,
|
|
|
Цитата Qraizer @ Это не обычная перегрузка. Это перегрузка с целью запрета генерировать эти спец.методы компилятором. Цель в том, чтобы запретить копирования и присваивания. Если их просто никак не объявить, компилятор способен будет сгенерировать их сам, если же они объявлены, то генерировать он уже ничего не будет. Но т.к. я их не определил, то при попытке скопировать или присвоить будет ошибка линковки. Т.е. цель запрета на эти операции достигнута, путь и несколько странным способом. Т.к. я не планирую их использовать, то и прототип может быть любым, Стандарт не запрещает возвращать что ни попадя. Конечно, для обычной перегрузки так писать не стоит, кроме очень специфичных случаев. В новых Стандартах для такого запрета есть более правильные методы, но на момент написания того кода их ещё не было, или поддерживающие новые фишки Стандарта компиляторы мне не были доступны. ![]() Что-то типа X& operator=(const X&) = delete; ? |
![]() |
Сообщ.
#25
,
|
|
Угу.
![]() |
![]() |
Сообщ.
#26
,
|
|
Цитата D_KEY @ Тут вся фишка в С++ в том, что политиками ты статически как бы "собираешь" свой тип, но при этом ты ничем не жертвуешь в динамкие. Т.е. как будто ты сам писал все эти классы руками делал. Т.е. абстракция остается исключительно в коде, а не в итоговой программе. Не думаю, что в других языках такое есть. В C# варианте, если IPolicy будут реализовывать структуры, а не классы, как в примере, то в ран тайм тоже не будет накладных расходов. |
Сообщ.
#27
,
|
|
|
Привет)
Справедливости ради, пример на C# был написан так, чтобы внешне быть похожим на плюсовый. На самом деле практической ценности в нём нет, так писать не стоит, дженерики тут не нужны. Достаточно передать в конструктор класса Foo нужный экземпляр ISync. И да, что-то "собрать" при компиляции можно с помощью кодогенерации (Roslyn, annotation processing, KSP и т.д), но тут это из пушки по воробьям) |
![]() |
Сообщ.
#28
,
|
|
Цитата IL_Agent @ Достаточно передать в конструктор класса Foo нужный экземпляр ISync. Вот как раз в этом случае у тебя будут потери на косвенный вызов метода через интерфейсную ссылку. Если это не парит, то и прекрасно. А во в случаях когда каждая микросекунда важна - дженерики - наше всё (в .NET) |
Сообщ.
#29
,
|
|
|
Цитата jack128 @ Вот как раз в этом случае у тебя будут потери на косвенный вызов метода через интерфейсную ссылку. Хочешь сказать, что вызов метода конкретного класса быстрее, чем вызов того же метода, но через интерфейс? Есть пруфы? |
![]() |
Сообщ.
#30
,
|
|
Для вызова метода интерфейса ж нужно сначала извлечь адрес метода из VMT и только потом сделать call, но тут разница микроскопическая. А вот что реально важно, так это inline. Если у нас в коде есть только интерфейс, то компилятор(JIT в случае .NET) не в курсе, что именно за метод должен вызваться и честно делает call <адрес метода> , вот если у нас есть структура мы вызваем myStruct.MyMethod(args) то jit сможет заинлайнить MyMethod со всеми вытекающими.
В .NET классический пример всего этого - это generic math в .NET 6 и ниже. Только после .NET7 таких примеров хрен нагуглишь :-D . А самому писать лень, сорри. |