Qore Programming Language  1.12.0
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");
}
The base class for all value and parse types in Qore expression trees.
Definition: AbstractQoreNode.h:57
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:48
This is the list container type in Qore, dynamically allocated only, reference counted.
Definition: QoreListNode.h:52
Qore's string value type, reference counted, dynamically-allocated only.
Definition: QoreStringNode.h:50

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);
#define QDOM_EXTERNAL_PROCESS
provides external process control functionality (can affect) start) or stop external processes)
Definition: Restrictions.h:162
#define QDOM_PROCESS
provides process control functionality (can affect or stop the current process)
Definition: Restrictions.h:160

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);
}
DLLEXPORT int64 getAsBigInt() const
returns the 64-bit integer value of the object
DLLEXPORT double getAsFloat() const
returns the float value of the object
long long int64
64bit integer type, cannot use int64_t here since it breaks the API on some 64-bit systems due to equ...
Definition: common.h:260

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
// call a function with the "const char *" to the string buffer of str
do_something(str->getBuffer());
return 0;
}
this class is used to safely manage calls to AbstractQoreNode::getStringRepresentation() when a simpl...
Definition: QoreStringNode.h:309

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;
}
DLLEXPORT int getMsZeroInt(QoreValue a)
for getting an integer number of milliseconds, with 0 as the default, from either a relative time val...
static bool is_nothing(const AbstractQoreNode *n)
to check if an AbstractQoreNode object is NOTHING
Definition: QoreLib.h:316
the implementation of Qore's object data type, reference counted, dynamically-allocated only
Definition: QoreObject.h:60

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;
}
DLLEXPORT AbstractQoreNode * refSelf() const
returns "this" with an incremented reference count

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;
}
DLLEXPORT int getAsInt() const
returns the integer value of the object
DLLEXPORT AbstractQoreNode * raiseException(const char *err, const char *fmt,...)
appends a Qore-language exception to the list