Custom functions are internally represented by struct Sass_C_Function_Descriptor
.
struct Sass_C_Function_Descriptor { const char* signature; Sass_C_Function function; void* cookie; };
signature
: The function declaration, like foo($bar, $baz:1)
function
: Reference to the C function callbackcookie
: any pointer you want to attachThe signature defines how the function can be invoked. It also declares which arguments are required and which are optional. Required arguments will be enforced by LibSass and a Sass error is thrown in the event a call as missing an argument. Optional arguments only need to be present when you want to overwrite the default value.
foo($bar, $baz: 2)
In this example, $bar
is required and will error if not passed. $baz
is optional and the default value of it is 2. A call like foo(10)
is therefore equal to foo(10, 2)
, while foo()
will produce an error.
The callback function needs to be of the following form:
union Sass_Value* call_sass_function( const union Sass_Value* s_args, void* cookie ) { return sass_clone_value(s_args); }
The cookie can hold any pointer you want. In the perl-libsass
implementation it holds the structure with the reference of the actual registered callback into the perl interpreter. Before that call perl-libsass
will convert all Sass_Values
to corresponding perl data types (so they can be used natively inside the perl interpretor). The callback can also return a Sass_Value
. In perl-libsass
the actual function returns a perl value, which has to be converted before libsass
can work with it again!
// allocate memory (copies passed strings) union Sass_Value* make_sass_boolean (int val); union Sass_Value* make_sass_number (double val, const char* unit); union Sass_Value* make_sass_color (double r, double g, double b, double a); union Sass_Value* make_sass_string (const char* val); union Sass_Value* make_sass_list (size_t len, enum Sass_Separator sep); union Sass_Value* make_sass_map (size_t len); union Sass_Value* make_sass_null (); union Sass_Value* make_sass_error (const char* msg); // Make a deep cloned copy of the given sass value union Sass_Value* sass_clone_value (const union Sass_Value* val); // deallocate memory (incl. all copied memory) void sass_delete_value (const union Sass_Value* val);
#include <stdio.h> #include <stdint.h> #include "sass/context.h" union Sass_Value* call_fn_foo(const union Sass_Value* s_args, void* cookie) { // we actually abuse the void* to store an "int" return sass_make_number((size_t)cookie, "px"); } int main( int argc, const char* argv[] ) { // get the input file from first argument or use default const char* input = argc > 1 ? argv[1] : "styles.scss"; // create the file context and get all related structs struct Sass_File_Context* file_ctx = sass_make_file_context(input); struct Sass_Context* ctx = sass_file_context_get_context(file_ctx); struct Sass_Options* ctx_opt = sass_context_get_options(ctx); // allocate a custom function caller Sass_C_Function_Callback fn_foo = sass_make_function("foo()", call_fn_foo, (void*)42); // create list of all custom functions Sass_C_Function_List fn_list = sass_make_function_list(1); sass_function_set_list_entry(fn_list, 0, fn_foo); sass_option_set_c_functions(ctx_opt, fn_list); // context is set up, call the compile step now int status = sass_compile_file_context(file_ctx); // print the result or the error to the stdout if (status == 0) puts(sass_context_get_output_string(ctx)); else puts(sass_context_get_error_message(ctx)); // release allocated memory sass_delete_file_context(file_ctx); // exit status return status; }
gcc -c main.c -o main.o gcc -o sample main.o -lsass echo "foo { margin: foo(); }" > foo.scss ./sample foo.scss => "foo { margin: 42px }"