## -*- tcl -*- # # ## ### ##### ######## ############# ##################### # Pragmas for MetaData Scanner. # n/a # CriTcl Utility Commands. Generation of functions handling conversion # from and to a C enum. Not a full Tcl_ObjType. Based on # Tcl_GetIndexFromObj() instead. package provide critcl::enum 1.2 # # ## ### ##### ######## ############# ##################### ## Requirements. package require Tcl 8.6 ; # Min supported version. package require critcl 3.1.11 ; # make, include -- dict portability package require critcl::literals 1.1 ; # String pool for conversion to Tcl. namespace eval ::critcl::enum {} # # ## ### ##### ######## ############# ##################### ## API: Generate the declaration and implementation files for the enum. proc ::critcl::enum::def {name dict {use tcl}} { # Arguments are # - the C name of the enumeration, and # - dict of strings to convert. Key is the symbolic C name, value # is the string. Numeric C value is in the order of the strings in # the dict, treating it as list for that case. # # dict: C symbolic name -> Tcl string (Tcl symbolic name). if {![dict size $dict]} { return -code error -errorcode {CRITCL ENUM DEF INVALID} \ "Expected an enum definition, got empty string" } set plist 0 foreach m $use { switch $m { tcl {} +list { set plist 1 } default { return -code error -errorcode {CRITCL ENUM DEF MODE INVALID} \ "Unknown mode $m, expected one of \"+list\", or \"tcl\"" } } } critcl::literals::def ${name}_pool $dict $use # _pool_names = C enum of symbolic names, and implied numeric values. # _pool.h = Header # _pool ( interp, code ) => Tcl_Obj* :: up-conversion C to Tcl. # Exporting: # Header .h # Function _ToObj (interp, code) -> obj # Function _ToObjList (interp, count, code*) -> obj (**) # Function _GetFromObj (interp, obj, flags, &code) -> Tcl code # Enum type _names # # (**) Mode +list only. dict for {sym str} $dict { lappend table "\t\t\"$str\"," } lappend map @NAME@ $name lappend map @TABLE@ \n[join $table \n] lappend map @TSIZE@ [llength $table] lappend map @TSIZE1@ [expr {1 + [llength $table]}] if {$plist} { lappend map @PLIST@ \ "\n #define ${name}_ToObjList(i,c,l) (${name}_pool_list(i,c,l))" } else { lappend map @PLIST@ "" } critcl::include [critcl::make ${name}.h \n[critcl::at::here!][string map $map { #ifndef @NAME@_HEADER #define @NAME@_HEADER #include <@NAME@_pool.h> #include typedef @NAME@_pool_names @NAME@; #define @NAME@_LAST @NAME@_pool_name_LAST extern int @NAME@_GetFromObj (Tcl_Interp* interp, Tcl_Obj* obj, int flags, int* literal); #define @NAME@_ToObj(i,l) (@NAME@_pool(i,l))@PLIST@ #endif }]] # Create second function, down-conversion Tcl to C. critcl::ccode [critcl::at::here!][string map $map { extern int @NAME@_GetFromObj (Tcl_Interp* interp, Tcl_Obj* obj, int flags, int* literal ) { static const char* strings[@TSIZE1@] = {@TABLE@ NULL }; return Tcl_GetIndexFromObj (interp, obj, strings, "@NAME@", flags, literal); } }] # V. Define convenient argument- and result-type definitions # wrapping the de- and encoder functions for use by cprocs. critcl::argtype $name \n[critcl::at::here!][string map $map { if (@NAME@_GetFromObj (interp, @@, TCL_EXACT, &@A) != TCL_OK) return TCL_ERROR; }] int int critcl::argtype ${name}-prefix \n[critcl::at::here!][string map $map { if (@NAME@_GetFromObj (interp, @@, 0, &@A) != TCL_OK) return TCL_ERROR; }] int int # Use the underlying literal pool directly. critcl::resulttype $name = ${name}_pool return } # # ## ### ##### ######## ############# ##################### ## Export API namespace eval ::critcl::enum { namespace export def catch { namespace ensemble create } } namespace eval ::critcl { namespace export enum catch { namespace ensemble create } } # # ## ### ##### ######## ############# ##################### ## Ready return