Qore Builtin Functions

# Qore Builtin Functions

To add a builtin function to the library, you have to call BuiltinFunctionList::add(). The BuiltinFunctionList class only contains static functions, but it can also be accessed through the global `builtinFunctions` object.

Builtin functions must have the following signature:

// "testfunction()" ignores all arguments and returns the string "testing"
AbstractQoreNode *f_testfunction(const QoreListNode *params, ExceptionSink *xsink)
{
return new QoreStringNode("testing");
}

Then the function can be added to the library's builtin function list with the following command:

builtinFunctions.add("testfunction", f_testfunction);

When adding a function that provides networking functionality, process control, filesystem access, threading functionality, or any other functionality that may potentially need to be restricted in QoreProgram objects, tag the function with one of the QDOM_* constants in Restrictions.h by including the domain constant as the third parameter to the BuiltinFunctionList::add() call. If a function provides functionality that falls into more than one domain, then binary or them together as in the following example:

// the exec() function provides access to external processes and also affects the current process
builtinFunctions.add("exec", f_exec, QDOM_EXTERNAL_PROCESS | QDOM_PROCESS);

## Handling Function Arguments

Use the inline functions in params.h to access function arguments. These are:

- num_params() - returns the number of arguments passed to the function
- get_param() - returns the argument in the position given or 0 if there is none

The following inline functions provide arguments of a specific type:

- test_binary_param() - returns a const BinaryNode pointer or 0
- test_string_param() - returns a const QoreStringNode pointer or 0
- test_object_param() - returns a QoreObject pointer or 0
- test_date_param() - returns a const DateTimeNode pointer or 0
- test_hash_param() - returns a const QoreHashNode pointer or 0
- test_list_param() - returns a const QoreListNode pointer or 0
- test_funcref_param() - returns a const ResolvedFunctionReferenceNode pointer or 0
- test_reference_param() - returns a const ReferenceNode pointer or 0
- test_nothing_param() - returns true if there is no value at the given position

Note that there are no functions for the integer (NT_INT) and floating-point (NT_FLOAT) types. These values should be acquired as necessary using the AbstractQoreNode::getAsInt(), AbstractQoreNode::getAsBigInt(), and AbstractQoreNode::getAsFloat() functions to allow for transparent type conversion from other data types.

Here are some examples:

AbstractQoreNode *f_testfunction(const QoreListNode *params, ExceptionSink *xsink)
{
// get the first integer argument
const AbstractQoreNode *p = get_param(params, 0);
int64 i = p ? p->getAsBigInt() : 0;
// get the second floating-point argument
p = get_param(params, 1);
double f = p ? p->getAsFloat() : 0.0;
int64 rc = do_something(i, f);
// returns an integer value
return new QoreBigIntNode(rc);
}

To use a string value, but accept conversion from other data types, use the QoreStringValueHelper class. This class is best for getting QoreString values or simply getting a "char *" for the converted value. The QoreStringValueHelper class also provides a constructor that allows the target character encoding to be specified (there is an example in the class documentation). If you need a QoreStringNode (NT_STRING) value, then use the QoreStringNodeValueHelper class instead.

Here is an example of interpreting a function argument as a string value using the QoreStringValueHelper class:

AbstractQoreNode *f_testfunction(const QoreListNode *params, ExceptionSink *xsink)
{
// get a pointer to the first argument
const AbstractQoreNode *p = get_param(params, 0);
// this will convert p to a string if necessary
QoreStringValueHelper str(p);
// call a function with the "const char *" to the string buffer of str
do_something(str->getBuffer());
return 0;
}

To process arguments meant to be timeout values (or meant to specify a period of time) where an integer implies certain units (such as milliseconds or seconds) but also allow relative DateTimeNode (NT_DATE) values to be processed, use the following functions:

- getSecZeroInt(const AbstractQoreNode *a)
- getSecZeroBigInt(const AbstractQoreNode *a)
- getSecMinusOneInt(const AbstractQoreNode *a)
- getSecMinusOneBigInt(const AbstractQoreNode *a)
- getMsZeroInt(const AbstractQoreNode *a)
- getMsZeroBigInt(const AbstractQoreNode *a)
- getMsMinusOneInt(const AbstractQoreNode *a)
- getMsMinusOneBigInt(const AbstractQoreNode *a)
- getMicroSecZeroInt(const AbstractQoreNode *a)

Each of the above functions returns an integer, assuming a certain unit for non DateTimeNode values, and also returns a default value if no argument was present (either 0 or -1).

The following is an example of using getMsZeroInt():

static AbstractQoreNode *MUTEX_lock(QoreObject *self, SmartMutex *m, const QoreListNode *params, ExceptionSink *xsink)
{
// the the first argument
const AbstractQoreNode *p = get_param(params, 0);
// we only return a return value if we have a timeout, otherwise we save allocating a QoreBigIntNode
if (!is_nothing(p)) {
// get the timeout value in milliseconds from the argument
int timeout_ms = getMsZeroInt(p);
// grab the lock with a timeout value
int rc = m->grab(xsink, timeout_ms);
// return the return code if there was no exception
if (!*xsink)
return new QoreBigIntNode(rc);
}
else // grab the lock unconditionally (without a timeout)
m->grab(xsink);
return 0;
}

## Function Return Value

Each Qore function should return a pointer to an AbstractQoreNode giving the return value of the function. If the function does not return a value, then it should simply return 0 as follows:

// "testfunction()" ignores all arguments and returns no value
AbstractQoreNode *f_testfunction(const QoreListNode *params, ExceptionSink *xsink)
{
return 0;
}

Otherwise, the pointer's reference count will be owned by the caller of builtin function, so returning a descendent of AbstractQoreNode created with the C++ new operator is OK, otherwise you have to make sure that the value returned by the builtin function is referenced for the return. In other words, returning AbstractQoreNode descendents that have an incremented reference count is OK, but, for example, to return an argument of the function as the return value of the function, you have to increment the reference count manually – the easiest way to do this is to call AbstractQoreNode::refSelf() on the value to be returned, as in the following example:

static AbstractQoreNode *f_return_first_argument(const QoreListNode *params, ExceptionSink *xsink)
{
const AbstractQoreNode *p = get_param(params, 0);
return p ? p->refSelf() : 0;
}

See the section below on Handling Qore Data for more information.

## Raising Exceptions in a Function

If your function raises an exception, then you must call ExceptionSink::raiseException() against the ExceptionSink argument to the function. In the case that your function raises a Qore-language exception, the function must always return 0 as a return value. The following is an example:

static AbstractQoreNode *f_remove_signal_handler(const QoreListNode *params, ExceptionSink *xsink)
{
const AbstractQoreNode *p0 = get_param(params, 0);
int signal = p0 ? p0->getAsInt() : 0;
if (!signal || signal > QORE_SIGNAL_MAX) {
xsink->raiseException("REMOVE-SIGNAL-HANDLER-ERROR", "%d is not a valid signal", signal);
return 0;
}
QoreSignalManager::removeHandler(signal, xsink);
return 0;
}