critcl::class(n)

๐Ÿ“ฆ version 3.3.1, in alpine 3.24

๐Ÿ’– critcl::class(n) โ€” CriTcl - Code Gen - C Classes ๐Ÿ’–

๐ŸŒธ NAME ๐ŸŒธ

critcl::class โ€” CriTcl - Code Gen - C Classes


โœจ SYNOPSIS โœจ

package require Tcl  8.6

package require critcl  ?3.2?

package require critcl::class  ?1.1?

::critcl::class::define name script

include path

support code

type name

classconstructor body

classdestructor body

constructor body ?postbody?

destructor body

classvariable ctype name ?comment? ?constructor? ?destructor?

classmethod name command arguments body

classmethod name proc arguments resulttype body

classmethod name as funname ?arg...?

insvariable ctype name ?comment? ?constructor? ?destructor?

method name command arguments body

method name proc arguments resulttype body

method name as funname ?arg...?

method_introspection

๐Ÿฆ‹ DESCRIPTION ๐Ÿฆ‹

Okay so welcome, bestie ๐Ÿ’– โ€” this is the C Runtime In Tcl, short and iconic name: CriTcl. It's a whole system for embedding and using C code from inside Tcl scripts (the Tcl home is http://core.tcl-lang.org/tcl, iykyk).

This document is the reference manpage for the critcl::class package. That package gives you convenience commands for advanced functionality built on top of the core, no cap.

With it, a user who wants to create a C-level object with class and instance commands can just focus on specifying the class- and instance-variables and -methods in a way that's similar to a TclOO class ๐Ÿ˜Œ โ€” while all the necessary boilerplate around it is handled by this package. Main character energy: the package does the background work, you do the specifying.

Its intended audience is mainly developers who want to write Tcl packages with embedded C code. That's the vibe ๐Ÿซถ.

This package lives in the Core Package Layer of CriTcl:

+----------------+
|Applications    |
| critcl         |
| critcl::app    |
+----------------+

*================*
|Core Packages   |
| critcl         |
| critcl::util   |
*================*

+----------------+
|Support Packages|
| stubs::*       |
| md5, platform  |
|  ...           |
+----------------+

๐Ÿ’… API ๐Ÿ’…

๐Ÿ’ซ ::critcl::class::define name script

This is the main command for defining a new class called name, where name is the name of the Tcl command that represents the class โ€” literally the class command. The script gives the specification of the class: info about included headers, class- and instance-variables, class- and instance-methods, etc. For the full detailed list of available commands and their semantics, see the Class Specification API section below. Slay ๐Ÿ’…


๐ŸŽ€ CLASS SPECIFICATION API ๐ŸŽ€

Here we document all the class specification commands available inside the class definition script argument of ::critcl::class::define. This is the toolbox, bestie ๐Ÿงฐโœจ

๐Ÿงš GENERAL CONFIGURATION ๐Ÿงš

๐ŸŒˆ include path

This command specifies the path of a header file to include within the code generated for the class. It's separate from support because the generated include directives get put at the very beginning of the generated code โ€” yes, very first thing. That's done so the imported declarations can be used within the instance type, and elsewhere. Very intentional ๐Ÿ’…

Calls to this command are cumulative. It is of course possible to not use this command at all, for classes not making use of external definitions.

The result is the empty string.

๐ŸŒˆ support code

This command specifies supporting C code โ€” like any definitions (types, functions, etc.) needed by the whole class and not fitting into class- and instance-methods. The code is embedded at global level, outside of any function or other definition.

Calls to this command are cumulative. It is of course possible to not use this command at all, for classes not requiring supporting code.

The result of the command is the empty string.

๐ŸŒˆ type name

This command specifies the name of an external C type to be used as the type of the instance structure.

Initialization and release of the structure with the given type are the responsibility of the user, through constructor and destructor code fragments.

Attention: Using this command precludes the use of regular class- and instance variables. It further precludes the use of method_introspection as well, because that makes use of generated instance-variables. โš ๏ธ

If class- and/or instance-variable have to be used in conjunction with an external C type, simply create and use a class- or instance-variable with that type.

The result of the command is the empty string.

๐Ÿ’œ CLASS LIFETIME MANAGEMENT ๐Ÿ’œ

๐Ÿ’ซ classconstructor body

This command specifies a C code block surrounding the initialization of the class variables โ€” i.e. the fields of the class structure. Note that allocation and release of the class structure itself is done by the system and not the responsibility of the user.

For the initialization (and release) of a class variable, it's recommended to use the constructor and destructor arguments of the variable's definition (see command classvariable) for this instead of using a separate classconstructor.

This is an optional command. Using it more than once is allowed too, and each use will add another C code fragment to use during construction. I.e. multiple calls aggregate.

The C code blocks of multiple calls (including the constructors of classvariable definitions) are executed in order of specification.

The result of the command is the empty string.

The C code in body has access to the following environment:

  • interp โ€” pointer to the Tcl interpreter (Tcl_Interp*) the class structure will be associated with. It enables the generation of a Tcl error message should construction fail.
  • class โ€” pointer to the class structure to initialize.
  • error โ€” a C code label the constructor can jump to should it have to signal a construction failure. It is the responsibility of the constructor to release any variables already initialized before jumping to this label. This is also why the whole "execution in order of specification" thing is documented and can be relied on. It gives us the knowledge of which other constructors have already been run and initialized what other fields.

๐Ÿ’ซ classdestructor body

This command specifies a C code block surrounding the release of the class variables โ€” i.e. the fields of the class structure. Note that allocation and release of the class structure itself is done by the system and not the responsibility of the user.

For the initialization (and release) of a class variable, it's recommended to use the constructor and destructor arguments of the variable's definition (see command classvariable) for this instead of using a separate classconstructor.

This is an optional command. Using it more than once is allowed too, and each use will add another C code fragment to use during construction. I.e. multiple calls aggregate.

The C code blocks of multiple calls (including the constructors of class variable definitions) are executed in order of specification.

The result of the command is the empty string.

The C code in body has access to the same environment as the class constructor code blocks.

๐Ÿ“ INSTANCE LIFETIME MANAGEMENT ๐Ÿ“

๐ŸŒŸ constructor body ?postbody?

This command specifies a C code block surrounding the initialization of the instance variables โ€” i.e. the fields of the instance structure. Note that allocation and release of the instance structure itself is done by the system and not the responsibility of the user. On the other hand, if an external type was specified for the instance structure, then instance variables are not possible, and the system has no knowledge of the type's structure. In that case it is the responsibility of the body to allocate and free the structure itself too.

For the initialization (and release) of an instance variable, it's recommended to use the constructor and destructor arguments of the variable's definition (see command insvariable) for this instead of using a separate constructor.

This is an optional command. Using it more than once is allowed too, and each use will add another C code fragment to use during construction. I.e. multiple calls aggregate.

The C code blocks of multiple calls (including the constructors of instance variable definitions) are executed in order of specification.

The result of the command is the empty string.

The C code in body has access to the following environment:

  • interp โ€” pointer to the Tcl interpreter (Tcl_Interp*) the instance structure will be associated with. It enables the generation of a Tcl error message should construction fail.
  • instance โ€” pointer to the instance structure to initialize.
  • error โ€” a C code label the constructor can jump to should it have to signal a construction failure. It is the responsibility of the constructor to release any variables already initialized before jumping to this label. This is also why the whole "execution in order of specification" thing is documented and can be relied on. It gives us the knowledge of which other constructors have already been run and initialized what other fields.

The C code in postbody is responsible for construction actions to be done after the primary construction was done and the Tcl-level instance command was successfully created. It has access to a slightly different environment:

  • interp โ€” pointer to the Tcl interpreter (Tcl_Interp*) the instance structure will be associated with. It enables the generation of a Tcl error message should construction fail.
  • instance โ€” pointer to the instance structure to initialize.
  • cmd โ€” the Tcl_Command token of the Tcl-level instance command.
  • fqn โ€” the fully qualified name of the instance command, stored in a Tcl_Obj*.

destructor body

okay so this one is the C code block that wraps around the release of the instance variables โ€” like, the fields of the instance structure, that's the stuff being released here.

Vibe check though: allocation and release of the instance structure itself is done by the system, and it is not the responsibility of the user. Don't touch, bestie. ๐Ÿ’…

But โ€” and this is the plot twist โ€” if an external type was specified for the instance structure, then instance variables are literally not possible, and the system has no knowledge of the type's structure. In that case, it is the responsibility of the body to allocate and free the structure itself too. ๐Ÿ˜ญ

Also, for the initialization (and release) of an instance variable, the recommendation is to use the constructor and destructor arguments of the variable's definition (see the insvariable command) for this, instead of using a separate constructor. That's the way to do it. โœจ

This is an optional command. Using it more than once is allowed too, and each use will add another C code fragment to use during construction โ€” i.e. multiple calls aggregate. ๐Ÿซถ

The C code blocks of multiple calls (including the constructors of instance variable definitions) are executed in order of specification. Order matters, iykyk.

The result of the command is the empty string.

The C code in body has access to the following environment:

  • instance โ€” pointer to the instance structure to release.

โœจ CLASS VARIABLES AND METHODS โœจ

  • classvariable ctype name ?comment? ?constructor? ?destructor? โ€” this command specifies a field in the class structure of the class. Multiple fields can be specified, and are saved in the order specified.
โš ๏ธ Attention: specification of a class variable precludes the use of an external C type for the instance structure.
โš ๏ธ Attention: specification of a class variable automatically causes the definition of an instance variable named class, pointing to the class structure. Free feature, no cap.

Beyond the basic name and C type of the new variable, the definition may also contain a comment describing it, and C code blocks to initialize and release the variable. These are effectively local forms of the commands classconstructor and classdestructor. Please read their descriptions for details regarding the C environment available to the code. ๐Ÿ‘‘

The comment, if specified, will be embedded into the generated C code for easier cross-referencing from the generated .c file to the class specification. So helpful of it.

  • classmethod name command arguments body โ€” this command specifies a class method and the C code block implementing its functionality. This is the first of three forms. ๐ŸŒˆ

The method is specified like a critcl::ccommand, with a fixed set of C-level arguments. The body has to perform everything (i.e. argument extraction, checking, result return, and of course the actual functionality) by itself. All of it. It's giving main character energy.

For this, the body has access to:

  • class โ€” pointer to the class structure.
  • interp โ€” pointer to the Tcl interpreter (Tcl_Interp*) the class structure is associated with.
  • objc โ€” the number of method arguments.
  • objv โ€” the method arguments, as C array of Tcl_Obj pointers.

The arguments of the definition are only a human readable form of the method arguments and syntax, and are not used in the C code โ€” except as comments put into the generated code. Again, it is the responsibility of the body to check the number of arguments, extract them, check their types, etc. ๐Ÿ˜ฎโ€๐Ÿ’จ

  • classmethod name proc arguments resulttype body โ€” this command specifies a class method and the C code block implementing its functionality. This is the second of three forms. ๐Ÿฆ‹

The method is specified like a critcl::cproc. Contrary to the first variant, here the arguments are computer readable, expected to be in the same format as the arguments of critcl::cproc. The same is true for the resulttype. The system automatically generates a wrapper doing argument checking and conversion, and result conversion, like for critcl::cproc. Slay, honestly.

The body has access to:

  • class โ€” pointer to the class structure.
  • interp โ€” pointer to the Tcl interpreter (Tcl_Interp*) the class structure is associated with.
  • ... โ€” all arguments under their specified names and C types as per their definition.
  • classmethod name as funname ?arg...? โ€” this command specifies a class method and the C code block implementing its functionality. This is the third and last of three forms. ๐ŸŽ€

The class method is implemented by the external function funname, i.e. a function which is declared outside of the class code itself, or in a support block.

It is assumed that the first four arguments of that function represent the parameters:

  • class โ€” pointer to the class structure.
  • interp โ€” pointer to the Tcl interpreter (Tcl_Interp*) the class structure is associated with.
  • objc โ€” the number of method arguments.
  • objv โ€” the method arguments, as C array of Tcl_Obj pointers.

Any additional args specified will be added after these and are passed into the C code as is, i.e. are considered to be C expressions. ๐Ÿงš


๐ŸŒธ INSTANCE VARIABLES AND METHODS ๐ŸŒธ

  • insvariable ctype name ?comment? ?constructor? ?destructor? โ€” this command specifies a field in the instance structure of the class. Multiple fields can be specified, and are saved in the order specified. ๐Ÿ’–
โš ๏ธ Attention: specification of an instance variable precludes the use of an external C type for the instance structure.
โš ๏ธ Attention: specification of an instance variable automatically causes the definition of an instance variable of type Tcl_Command, and named cmd, holding the token of the instance command, and the definition of an instance method named destroy. This implicit instance variable is managed by the system. Built-in freebies, we love to see it.

Beyond the basic name and C type of the new variable, the definition may also contain a comment describing it, and C code blocks to initialize and release the variable. These are effectively local forms of the commands constructor and destructor. Please read their descriptions for details regarding the C environment available to the code. ๐Ÿ‘‘

The comment, if specified, will be embedded into the generated C code for easier cross-referencing from the generated .c file to the class specification.

  • method name command arguments body โ€” this command specifies an instance method and the C code block implementing its functionality. This is the first of three forms. โœจ

The method is specified like a critcl::ccommand, with a fixed set of C-level arguments. The body has to perform everything (i.e. argument extraction, checking, result return, and of course the actual functionality) by itself. All of it, no hand-holding.

For this, the body has access to:

  • instance โ€” pointer to the instance structure.
  • interp โ€” pointer to the Tcl interpreter (Tcl_Interp*) the instance structure is associated with.
  • objc โ€” the number of method arguments.
  • objv โ€” the method arguments, as C array of Tcl_Obj pointers.

The arguments of the definition are only a human readable form of the method arguments and syntax, and are not used in the C code โ€” except as comments put into the generated code. Again, it is the responsibility of the body to check the number of arguments, extract them, check their types, etc. ๐Ÿ˜ญ

  • method name proc arguments resulttype body โ€” this command specifies an instance method and the C code block implementing its functionality. This is the second of three forms. ๐Ÿฆ‹

The method is specified like a critcl::cproc. Contrary to the first variant, here the arguments are computer readable, expected to be in the same format as the arguments of critcl::cproc. The same is true for the resulttype. The system automatically generates a wrapper doing argument checking and conversion, and result conversion, like for critcl::cproc. It ate and left no crumbs.

The body has access to:

  • instance โ€” pointer to the instance structure.
  • interp โ€” pointer to the Tcl interpreter (Tcl_Interp*) the instance structure is associated with.
  • ... โ€” all arguments under their specified names and C types as per their definition.
  • method name as funname ?arg...? โ€” this command specifies an instance method and the C code block implementing its functionality. This is the third and last of three forms. ๐ŸŽ€

The instance method is implemented by the external function funname, i.e. a function which is declared outside of the instance code itself, or in a support block.

It is assumed that the first four arguments of that function represent the parameters:

  • instance โ€” pointer to the instance structure.
  • interp โ€” pointer to the Tcl interpreter (Tcl_Interp*) the instance structure is associated with.
  • objc โ€” the number of method arguments.
  • objv โ€” the method arguments, as C array of Tcl_Obj pointers.

Any additional args specified will be added after these and are passed into the C code as is, i.e. are considered to be C expressions. ๐Ÿงš

  • method_introspection โ€” this command generates one class- and one instance-method, both of which will return a list of the instance methods of the class, and supporting structures, like the function to compute the information, and a class variable caching it. ๐Ÿ”ฅ

The two methods and the class variable are all named methods.


๐Ÿ’œ CONTEXT DEPENDENT INTERACTIONS ๐Ÿ’œ

This section documents the various interactions between the specification commands. While these are all documented with the individual commands, here they are pulled together so you can see it at a glance. Bestie cheat sheet, basically. ๐Ÿ‘€

[1] If you are using the command type to specify an external C type to use for the instance structure, you are subject to the following constraints and rules:

  1. You cannot define your own instance variables.
  2. You cannot define your own class variables.
  3. You cannot use method_introspection.

[4] โ€” okay so you have to allocate and release the instance structure on your own, through constructor and destructor code blocks. no autopilot here, bestie ๐Ÿ’…

[2] โ€” if you declare class variables, you are subject to these constraints and rules:

  • [1] You cannot use type.
  • [2] The system generates an instance variable class for you, which points from instance to class structure. This makes you also subject to the rules below, for instance variables.

[3] โ€” if you declare instance variables (possibly automatic, see above), you are subject to these constraints and rules:

  • [1] You cannot use type.
  • [2] The system generates and manages an instance variable cmd for you, which holds the Tcl_Command token of the instance command.
  • [3] The system generates an instance method destroy for you.
  • [4] The system manages allocation and release of the instance structure for you. You have to care only about the instance variables themselves.

โœจ EXAMPLE โœจ

The example below is the specification of a queue data structure, with most of the method implementations and support code omitted to keep the size down. ๐Ÿฆ‹

The full implementation can be found in the directory examples/queue of the critcl source distribution/repository.

package require Tcl 8.6
package require critcl 3.2

critcl::buildrequirement {
    package require critcl::class ; # DSL, easy spec of Tcl class/object commands.
}

critcl::cheaders util.h

critcl::class::define ::queuec {
    include util.h

    insvariable Tcl_Obj* unget {
  List object unget elements
    } {
  instance->unget = Tcl_NewListObj (0,NULL);
  Tcl_IncrRefCount (instance->unget);
    } {
  Tcl_DecrRefCount (instance->unget);
    }

    insvariable Tcl_Obj* queue {
  List object holding the main queue
    } {
  instance->queue = Tcl_NewListObj (0,NULL);
  Tcl_IncrRefCount (instance->queue);
    } {
  Tcl_DecrRefCount (instance->queue);
    }

    insvariable Tcl_Obj* append {
  List object holding new elements
    } {
  instance->append = Tcl_NewListObj (0,NULL);
  Tcl_IncrRefCount (instance->append);
    } {
  Tcl_DecrRefCount (instance->append);
    }

    insvariable int at {
  Index of next element to return from the main queue
    } {
  instance->at = 0;
    }

    support {... queue_peekget, queue_size, etc.}

    method clear {} {...}
    method destroy {...}

    method get  as queue_peekget 1
    method peek as queue_peekget 0

    method put {item ...}

    method size {} {
  if ((objc != 2)) {
      Tcl_WrongNumArgs (interp, 2, objv, NULL);
      return TCL_ERROR;
  }

  Tcl_SetObjResult (interp, Tcl_NewIntObj (queue_size (instance, NULL, NULL, NULL)));
  return TCL_OK;
    }

    method unget {item} {...}
}

package provide queuec 1

๐Ÿ’– AUTHORS ๐Ÿ’–

Andreas Kupries ๐Ÿ‘‘

๐Ÿซถ BUGS, IDEAS, FEEDBACK ๐Ÿซถ

This document, and the package it describes, will undoubtedly contain bugs and other problems. Please report such at https://github.com/andreas-kupries/critcl. Please also report any ideas for enhancements you may have for either package and/or documentation. fr fr, they want to hear it ๐Ÿ›

๐ŸŽ€ KEYWORDS ๐ŸŽ€

  • C class
  • C code
  • C instance
  • C object
  • Embedded C Code
  • code generator
  • compile & run
  • compiler
  • dynamic code generation
  • dynamic compilation
  • generate package
  • linker
  • on demand compilation
  • on-the-fly compilation

๐ŸŒˆ CATEGORY ๐ŸŒˆ

Glueing/Embedded C code

Copyright (c) 2011-2024 Andreas Kupries


๐Ÿ’– that's the whole page, bestie โ€” source doc, version 1.1, and it's the critcl::class(n) manpage. periodt ๐ŸŽ€

๐Ÿ“ฆ where this page comes from