critcl::cutil(3tcl)

πŸ“¦
version 3.3.1 Β· 3 versions
, in ubuntu resolute, ubuntu stonking

πŸ‘‘ critcl::cutil β€” CriTcl - C-level Utilities πŸ‘‘

okay bestie, welcome to the 3tcl manual page for critcl::cutil πŸ’… β€” like, the C Runtime In Tcl (everyone calls it CriTcl) β€” and we're about to make your C-from-Tcl life so much easier. this is part one, so buckle up.


✨ SYNOPSIS ✨

package require Tcl 8.6

package require critcl ?3.2?

package require critcl::cutil ?0.3?

::critcl::cutil::alloc

::critcl::cutil::assertions ?enable?

::critcl::cutil::tracer ?enable?

type* ALLOC (type)

type* ALLOC_PLUS (type, int n)

type* NALLOC (type, int n)

type* REALLOC (type* var, type, int n)

void FREE (type* var)

void STREP (Tcl_Obj* o, char* s, int len)

void STREP_DS (Tcl_Obj* o, Tcl_DString* ds)

void STRDUP (varname, char* str)

void ASSERT (expression, char* message

void ASSERT_BOUNDS (int index, int size)

void STOPAFTER(n)

TRACE_ON

TRACE_OFF

TRACE_TAG_ON  (identifier)

TRACE_TAG_OFF (identifier)

void TRACE_FUNC

void TRACE_TAG_FUNC (tag)

void TRACE_FUNC_VOID

void TRACE_TAG_FUNC_VOID (tag)

void TRACE_RETURN_VOID

void TRACE_TAG_RETURN_VOID (tag)

any TRACE_RETURN     (     char* format, any x)

any TRACE_TAG_RETURN (tag, char* format, any x)

void TRACE     (     char* format, ...)

void TRACE_TAG (tag, char* format, ...)

void TRACE_HEADER (int indent)

void TRACE_TAG_HEADER (tag, int indent)

void TRACE_CLOSER

void TRACE_TAG_CLOSER (tag)

void TRACE_ADD          (const char* format, ...)

void TRACE_TAG_ADD (tag, const char* format, ...)

void TRACE_PUSH_SCOPE (const char* name)

void TRACE_PUSH_FUNC

void TRACE_PUSH_POP

TRACE_TAG_VAR (tag)

TRACE_RUN (code);

TRACE_DO (code);

TRACE_TAG_DO (tag, code);

🌈 DESCRIPTION 🌈

hi hello hiii, be welcome to the C Runtime In Tcl (short: CriTcl), a whole system for embedding and using C code from inside your Tcl scripts β€” yes, Tcl lives at [http://core.tcl-lang.org/tcl](http://core.tcl-lang.org/tcl) πŸ’–

this doc right here? it's the reference manpage for the critcl::cutil package. this package wraps up a bunch of C-level utilities that make it way easier to write memory allocations, assertions, and narrative tracing, and it hands you convenience commands so your critcl projects can actually reach these utilities. intended audience? mainly the developers out there writing Tcl packages with embedded C code. iykyk 🧚

this package lives in the Core Package Layer of CriTcl, like so:

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

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

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

and the reason it sits there? the main critcl package itself uses the narrative tracing facilities when critcl::config trace is set, so it can instrument commands and procedures. slay, honestly. πŸ’…


πŸŽ€ API πŸŽ€

πŸ¦‹ ::critcl::cutil::alloc

this command hands you a whole bunch of C-preprocessor macros that make writing memory allocations for structures (and arrays of structures) so much less painful. no cap.

when you run it, the header file "critcl_alloc.h" is made directly available to the .critcl file containing the command, and it also becomes available for use in #include directives of companion C code that you declared via critcl::csources.

the macro definitions and their signatures are:

type* ALLOC (type)
type* ALLOC_PLUS (type, int n)
type* NALLOC (type, int n)
type* REALLOC (type* var, type, int n)
void  FREE (type* var)

void STREP    (Tcl_Obj* o, char* s, int len);
void STREP_DS (Tcl_Obj* o, Tcl_DString* ds);
void STRDUP   (varname, char* str);

the full tea on what they actually do is in the Allocation section below ⬇️

the result of the command is an empty string.

πŸ“ ::critcl::cutil::assertions ?enable?

this one gives you C-preprocessor macros for writing assertions in your C code. periodt.

when invoked, the header file "critcl_assert.h" is made directly available to the .critcl file containing the command, and it becomes available for use in #include directives of companion C code declared via critcl::csources.

the macro definitions and their signatures are:

void ASSERT (expression, char* message);
void ASSERT_BOUNDS (int index, int size);

void STOPAFTER (int n);

now here's the important bit πŸ‘€ β€” these definitions are conditional on the existence of the macro CRITCL_ASSERT. if you don't pass a critcl::cflags -DCRITCL_ASSERT, then every assertion in the C code is quiescent, i.e. it's not compiled into the object file at all. so basically assertions can be (de)activated whenever you want, at build time, as needed by the user.

for convenience, enable controls all of this. by default (false) the facility is available, but not active. passing true doesn't just make it available β€” it activates it too. love that for us πŸ’–

the details of the semantics are explained in the Assertions section.

the result of the command is an empty string.

πŸ‘‘ ::critcl::cutil::tracer ?enable?

the tracer ✨ β€” this command gives you C-preprocessor macros for tracing C-level internals.

when invoked, the header file "critcl_trace.h" is made directly available to the .critcl file containing the command, and it becomes available for use in #include directives of companion C code declared via critcl::csources. and on top of that, the ".c" file containing the runtime support is added to the set of C companion files.

the macro definitions and their signatures are:

/* (de)activation of named logical streams.
 * These are declarators, not statements.
 */

TRACE_ON;
TRACE_OFF;
TRACE_TAG_ON  (tag_identifier);
TRACE_TAG_OFF (tag_identifier);

/*
 * Higher level trace statements (convenience commands)
 */

void TRACE_FUNC   (const char* format, ...);
void TRACE_FUNC_VOID;
any  TRACE_RETURN (const char* format, any x);
void TRACE_RETURN_VOID;
void TRACE (const char* format, ...);

/*
 * Low-level trace statements the higher level ones above
 * are composed from. Scope management and output management.
 */

void TRACE_PUSH_SCOPE (const char* scope);
void TRACE_PUSH_FUNC;
void TRACE_POP;

void TRACE_HEADER (int indent);
void TRACE_ADD (const char* format, ...);
void TRACE_CLOSER;

/*
 * Convert tag to the underlying status variable.
 */

TRACE_TAG_VAR (tag)

/*
 * Conditional use of arbitrary code.
 */

TRACE_RUN (code);
TRACE_DO (code);
TRACE_TAG_DO (code);

same deal as assertions, bestie β€” these definitions are conditional on the existence of the macro CRITCL_TRACER. without a critcl::cflags -DCRITCL_TRACER, all trace functionality in the C code is quiescent and not compiled into the object file. so tracing can be (de)activated at will during build time, as needed by the user.

for convenience, enable controls it. by default (false) the facility is available but not active; true makes it available and activates it. and also 🫢 β€” the command critcl::config now accepts a boolean option trace. setting it activates enter/exit tracing in all commands based on critcl::cproc, with proper printing of arguments and results. and that implicitly activates the tracing facility in general. main character energy, honestly.

the details of the semantics are explained in the Tracing section.

the result of the command is an empty string.


🧚 ALLOCATION 🧚

type* ALLOC (type)

this macro allocates a single element of the given type and returns a pointer to that memory. one and done πŸ’…

type* ALLOC_PLUS (type, int n)

this macro allocates a single element of the given type, plus an additional n bytes right after the structure, and returns a pointer to that memory.

it's for variable-sized structures. an example of such a thing could be a generic list element structure that stores management information in the structure itself, with the value/payload sitting immediately after it, in the same memory block. iconic behaviour, honestly.

type* NALLOC (type, int n)

this macro allocates n elements of the given type and returns a pointer to that memory.

type* REALLOC (type* var, type, int n)

this macro expands or shrinks the memory associated with the C variable var of type type, so it can hold n elements of that type. it returns a pointer to that memory.

⚠️ remember bestie β€” a reallocation may move the data to a new location in memory to satisfy the request. returning a pointer instead of immediately assigning it to var lets you validate the new pointer before you try to use it. do not skip that step, fr fr.

void FREE (type* var)

this macro releases the memory referenced by the pointer variable var. bye felicia πŸ¦‹

void STREP (Tcl_Obj* o, char* s, int len)

this macro properly sets the string representation of the Tcl object o to a copy of the string s, which is expected to be of length len.

void STREP_DS (Tcl_Obj* o, Tcl_DString* ds)

this macro properly sets the string representation of the Tcl object o to a copy of the string held by the DString ds.

void STRDUP (varname, char* str)

this macro duplicates the string str into the heap and stores the result into the named char* variable var.


πŸ’€ ASSERTIONS πŸ’€

void ASSERT (expression, char* message

this macro tests expression and panics if it doesn't hold. the specified message is used as part of the panic.

⚠️ heads up: the message has to be a static string β€” it cannot be a variable. no sneaky runtime strings here, bestie.

void ASSERT_BOUNDS (int index, int size)

this macro makes sure that index is in the range 0 to size-1. keep those bounds in check, always πŸ™ˆ

void STOPAFTER(n)

this macro throws a panic after it has been called n times.

πŸ‘€ note: each separate instance of the macro has its own counter. they do not share, okay?

more coming in part 2, don't go anywhere πŸ’–

πŸ¦‹ TRACING πŸ¦‹

Okay so bestie, here's the tea: all output is printed to stdout. That's it, that's the whole destination. ✨


πŸŽ€ Turning the streams on and off

  • TRACE_ON β€” turns tracing on πŸ’–
  • TRACE_OFF β€” turns tracing off πŸ’…
  • TRACE_TAG_ON (identifier) β€” turns a specific logical stream on
  • TRACE_TAG_OFF (identifier) β€” turns that specific stream back off

These "commands" are actually declarators, so no cap, they go outside of functions β€” don't try to be cute and put them inside one. They (de)activate specific logical streams, named either explicitly by the user, or implicitly, referring to the current file.

For example:

TRACE_TAG_ON (lexer_in);

Heads up 🫢: all the high- and low-level trace commands that produce output have the controlling tag as an implicit argument. The scope management commands do not take tags. iykyk. πŸ’…


🌸 Function entry and exit

  • void TRACE_FUNC
  • void TRACE_TAG_FUNC (tag)
  • void TRACE_FUNC_VOID
  • void TRACE_TAG_FUNC_VOID (tag)

Use these macros at the beginning of a C function to record entry into it, periodt. The name of the entered function is an implicit argument (__func__), which forces users to have a C99 compiler. No C99, no vibes, sorry babe 😭.

The tracer's runtime maintains a stack of active functions and expects that function return is signaled by either TRACE_RETURN, TRACE_RETURN_VOID, or the equivalent forms taking a tag. Like, it's counting on you. Don't ghost it. πŸ™ˆ


  • void TRACE_RETURN_VOID
  • void TRACE_TAG_RETURN_VOID (tag)

Use these macros instead of

return

to function. Beyond returning from the function this also signals the same to the tracer's runtime, popping the last entered function from its stack of active functions. Iconic behavior honestly. πŸ’–


  • any TRACE_RETURN ( char* format, any x)
  • any TRACE_TAG_RETURN (tag, char* format, any x)

Use this macro instead of

return x

to non-void function. Beyond returning from the function with value x this also signals the same to the tracer's runtime, popping the last entered function from its stack of active functions. The format is expected to be a proper formatting string for printf and analogues, able to stringify x. So like, don't hand it nonsense, bestie. 🌈


✨ Printing things

  • void TRACE ( char* format, ...)
  • void TRACE_TAG (tag, char* format, ...)

This macro is the trace facilities' equivalent of printf, printing arbitrary data under the control of the format. Slay. πŸ”₯

The printed text is closed with a newline, and indented as per the stack of active functions. So your output is always cute and aligned, no manual spacing required. πŸ’…


🧚 The low-level stuff (for when you're feeling fancy)

  • void TRACE_HEADER (int indent)
  • void TRACE_TAG_HEADER (tag, int indent)

This is the low-level macro which prints the beginning of a trace line. This prefix consists of:

  • physical location (file name and line number), if available
  • indentation as per the stack of active scopes (if activated)
  • the name of the active scope
  • void TRACE_CLOSER
  • void TRACE_TAG_CLOSER (tag)

This is the low-level macro which prints the end of a trace line. The little bow on top of your output πŸŽ€.

  • void TRACE_ADD (const char* format, ...)
  • void TRACE_TAG_ADD (tag, const char* format, ...)

This is the low-level macro which adds formatted data to the line. Sprinkle sprinkle ✨.


πŸ’œ Scope management

  • void TRACE_PUSH_SCOPE (const char* name)
  • void TRACE_PUSH_FUNC
  • void TRACE_PUSH_POP

These are the low-level macros for scope management. The first two forms push a new scope on the stack of active scopes, and the last forms pops the last scope pushed. Stack discipline, bestie, we love to see it 🫢.


⭐ Helper macros

  • TRACE_TAG_VAR (tag) β€” Helper macro converting from a tag identifier to the name of the underlying status variable. Like a little translator for your tags, no cap πŸ”₯
  • TRACE_RUN (code); β€” Conditionally insert the code at compile time when the tracing facility is activated. Compile-time only, so it costs you literally nothing when tracing is off. Main character energy. πŸ‘‘
  • TRACE_DO (code);
  • TRACE_TAG_DO (tag, code);

Insert the code at compile time when the tracing facility is activated, and execute the same when either the implicit tag for the file or the user-specified tag is active. So it's in the build and it actually runs when the right stream is live. Double vibe check, passed. βœ…


πŸ’– AUTHORS πŸ’–

Andreas Kupries


😭 BUGS, IDEAS, FEEDBACK 😭

This document, and the package it describes, will undoubtedly contain bugs and other problems. Like, undoubtedly. 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. We're listening, fr fr 🫢


🌸 KEYWORDS 🌸

C code, 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


Okay so that's the whole page bestie, you're officially a tracing expert, no cap πŸ’…βœ¨ Go forth and TRACE responsibly. And remember: always signal your returns, or the stack will be so confused. πŸ™ˆ

That's a wrap on critcl::cutil(3tcl) β€” this cutie lives in doc, sitting pretty at version 0.3, dated 2024, and it ate, honestly. πŸ¦‹πŸ’–βœ¨

πŸ“¦ where this page comes from