英文:
TclOO Logging Mixin
问题
I wrote this code (below) trying to figure out more about TclOO mixin classes.
a. is there a more direct way to write the concrete class name from the mixin constructor?
使用mixin构造函数更直接的方法是否存在?
b. is there a way to simplify the mixin class using self
requiring no additional calls (e.g. init
) other than mixin
(as below) when using it? (cross checked with https://stackoverflow.com/questions/50352363/tcloo-class-logger-mixin , not clear)
在使用mixin时,是否有一种方式可以简化mixin类,只使用
self
,而无需额外调用(例如init
),如下所示?(与https://stackoverflow.com/questions/50352363/tcloo-class-logger-mixin进行了交叉检查,不清楚)
package require TclOO
package require try
package require logger
namespace eval ::demo::utils {}
namespace eval ::demo::naming {}
::oo::class create ::demo::utils::LoggingMixin {
variable log
constructor {args} {
set concls [lindex [self call] 0 end end-1]
try {
set log [::logger::servicecmd $concls]
} trap {LOGGER NO_SUCH_SERVICE} {} {
set log [::logger::init $concls]
}
# check for next
if {[llength [self next]] > 0} {
next {*}$args
}
}
}
::oo::class create ::demo::naming::GenericNaming {
mixin ::demo::utils::LoggingMixin
variable log
constructor {args} {
${log}::info "hello world"
}
}
::oo::class create ::demo::naming::NewNaming {
mixin ::demo::utils::LoggingMixin
variable log
constructor {args} {
${log}::info "new world"
}
}
::demo::naming::GenericNaming new
::demo::naming::NewNaming new
expected output:
$ tclsh ./mixin.tcl
[Sun Jun 04 21:39:22 BST 2023] [::demo::naming::GenericNaming] [info] 'hello world'
[Sun Jun 04 21:39:22 BST 2023] [::demo::naming::NewNaming] [info] 'new world'
英文:
I wrote this code (below) trying to figure out more about TclOO mixin classes.
a. is there a more direct way to write the concrete class name from the mixin constructor?
> set concls [lindex [self call] 0 end end-1]
b. is there a way to simplify the mixin class using self
requiring no additional calls (e.g. init
) other than mixin
(as below) when using it? (cross checked with https://stackoverflow.com/questions/50352363/tcloo-class-logger-mixin , not clear)
> mixin ::demo::utils::LoggingMixin
package require TclOO
package require try
package require logger
namespace eval ::demo::utils {}
namespace eval ::demo::naming {}
::oo::class create ::demo::utils::LoggingMixin {
variable log
constructor {args} {
set concls [lindex [self call] 0 end end-1]
try {
set log [::logger::servicecmd $concls]
} trap {LOGGER NO_SUCH_SERVICE} {} {
set log [::logger::init $concls]
}
# check for next
if {[llength [self next]] > 0} {
next {*}$args
}
}
}
::oo::class create ::demo::naming::GenericNaming {
mixin ::demo::utils::LoggingMixin
variable log
constructor {args} {
${log}::info "hello world"
}
}
::oo::class create ::demo::naming::NewNaming {
mixin ::demo::utils::LoggingMixin
variable log
constructor {args} {
${log}::info "new world"
}
}
::demo::naming::GenericNaming new
::demo::naming::NewNaming new
expected output:
$ tclsh ./mixin.tcl
[Sun Jun 04 21:39:22 BST 2023] [::demo::naming::GenericNaming] [info] 'hello world'
[Sun Jun 04 21:39:22 BST 2023] [::demo::naming::NewNaming] [info] 'new world'
答案1
得分: 3
获取正在创建的对象的类,只需在构造函数中执行以下操作:
set concls [info object class [self]]
无需遍历参数(这很好,因为在存在复杂的转发或已覆盖new
和/或create
时,这样做会更容易出错)。
Tcl 8.6不需要您执行package require TclOO
或package require try
;这些都是内置功能。
我倾向于这样设置日志混合类:
oo::class create ::demo::utils::LoggingMixin {
variable log
constructor {args} {
set concls [info object class [self]]
try {
set log [::logger::servicecmd $concls]
} trap {LOGGER NO_SUCH_SERVICE} {} {
set log [::logger::init $concls]
}
next {*}$args
}
# TODO: 为其他所需的日志级别添加方法
method LogInfo args {
tailcall ${log}::info {*}$args
}
}
因为然后子类可以只是这样的:
oo::class create LoggingDemo {
mixin ::demo::utils::LoggingMixin
constructor {args} {
my LogInfo "new world"
}
}
或者甚至可以制作一些过程来进一步改进语法:
constructor {args} {
set concls [info object class [self]]
try {
set log [::logger::servicecmd $concls]
} trap {LOGGER NO_SUCH_SERVICE} {} {
set log [::logger::init $concls]
}
proc logInfo args {
variable log; # 需要明确指定这一点
tailcall ${log}::info {*}$args
}
next {*}$args
}
另一种方法是使混合类提供自我初始化的日志方法:
oo::class create ::demo::utils::LoggingMixin {
variable LoggingMixinLog
method GetLog {level} {
if {![info exist LoggingMixinLog]} {
set concls [info object class [self]]
try {
set LoggingMixinLog [::logger::servicecmd $concls]
} trap {LOGGER NO_SUCH_SERVICE} {} {
set LoggingMixinLog [::logger::init $concls]
}
}
return ${LoggingMixinLog}::$level
}
method LogInfo args {
tailcall [my GetLog info] {*}$args
}
}
oo::class create LoggingDemo {
mixin ::demo::utils::LoggingMixin
constructor {args} {
my LogInfo "new world"
}
}
这有助于在事后混合到对象或类中的支持。如果要创建日志包装器,建议使用tailcall
或uplevel 1
来调用它们,以便当前的堆栈帧是想要创建日志条目的堆栈帧,而不是小型代理帮助方法/过程。
英文:
To get the class of the object being created, it is sufficient to do this in the constructor:
set concls [info object class [self]]
You don't need to pick through the arguments (good, as that's more error-prone when there are complex forwardings in place or new
and/or create
have been overridden).
Tcl 8.6 doesn't need you to package require TclOO
or package require try
; both are built-in features these days.
I'd be inclined to set up the logging mixin like this:
oo::class create ::demo::utils::LoggingMixin {
variable log
constructor {args} {
set concls [info object class [self]]
try {
set log [::logger::servicecmd $concls]
} trap {LOGGER NO_SUCH_SERVICE} {} {
set log [::logger::init $concls]
}
next {*}$args
}
# TODO: Add methods for other wanted logging levels
method LogInfo args {
tailcall ${log}::info {*}$args
}
}
Because then the subclass could be just this:
oo::class create LoggingDemo {
mixin ::demo::utils::LoggingMixin
constructor {args} {
my LogInfo "new world"
}
}
Or I might even make some procedures to improve the syntax further:
constructor {args} {
set concls [info object class [self]]
try {
set log [::logger::servicecmd $concls]
} trap {LOGGER NO_SUCH_SERVICE} {} {
set log [::logger::init $concls]
}
proc logInfo args {
variable log; # Need to be explicit with this
tailcall ${log}::info {*}$args
}
next {*}$args
}
Another approach is to make the mixin provide logging methods that are self-initializing:
oo::class create ::demo::utils::LoggingMixin {
variable LoggingMixinLog
method GetLog {level} {
if {![info exist LoggingMixinLog]} {
set concls [info object class [self]]
try {
set LoggingMixinLog [::logger::servicecmd $concls]
} trap {LOGGER NO_SUCH_SERVICE} {} {
set LoggingMixinLog [::logger::init $concls]
}
}
return ${LoggingMixinLog}::$level
}
method LogInfo args {
tailcall [my GetLog info] {*}$args
}
}
oo::class create LoggingDemo {
mixin ::demo::utils::LoggingMixin
constructor {args} {
my LogInfo "new world"
}
}
That has the advantage of supporting mixing into objects or classes after the fact.
If you make logging wrappers, calling them with either tailcall
or uplevel 1
is advisable so that the current stack frame is the one that wants to make the log entry, not the little proxy helper method/procedure.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论