File: writing-functions.sgml

package info (click to toggle)
gnumeric 1.10.8-1squeeze5
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 90,968 kB
  • ctags: 23,303
  • sloc: ansic: 248,235; xml: 51,894; sh: 10,491; makefile: 2,822; perl: 2,466; yacc: 1,272; python: 205
file content (262 lines) | stat: -rw-r--r-- 8,595 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262

Howto write builtin functions for gnumeric, version 0.2
by Michael Meeks <mmeeks@gnu.org>


This text describes the API and conventions a programmer should use
to implement and register new functions for use in gnumeric.


-------------------------------------------------------------------

Functions in Gnumeric should be registered in a category, to do this
you must get a category handle:

FunctionCategory *cat = function_get_category (_("My functions"));

If the category already exists it will be returned, otherwise a new
category will be created. Then, you will have to add your functions
individualy to the category. For example, a simple routine to sum
two numbers, you would do:

        char *help_add2numbers = "ADD2NUMBERS: Adds two numbers";

This C declaration defines the HELP associated with this
function.

        function_add_args (cat, "add2numbers", "ff", "number1,number2",
	                   help_add2numbers, add2numbers);

[ NB. there are several types of function; more of this later ]

This defines our function, the paramters in order are:

'cat'          The category handle obtained earlier
'add2numbers'  The function name ( as used in gnumeric )
'ff'           The argument token type string, this tells gnumeric that
               we are expecting two floating point arguments.
'number1,...'  Symbolic names for the arguments (in case our function
               supports them, this allows users to specify named paramters)
'help_add2..'  This links in the help text
'add2numbers'  This is the name of the function that gnumeric will call
               to evaluate the function.

Now, our routine is pretty simple: it will always require two arguments
to be passed, so we can let Gnumeric deal with argument count and we
can let the Gnumeric engine compute the values in the expression for
us. If we had a more complex function we would need to use
function_add_nodes ( more later ).

When the user calls the function "ADD2NUMBERS" in a Gnumeric
expression, our routine will be invoked, this is how the add2numbers
function signature looks like:

static Value *
add2numbers (FunctionEvalInfo *ei, Value *argv [])
{
	
}

The function should return a newly allocated Value structure.
Arguments are passed in the argv array and they contain Value
structures (look in gnumeric/src/value.h for the definition of the
_Value union, which is typedef'ed to Value in gnumeric.h).

The first parameter, ei is a pointer to the FunctionEvalInfo
structure, this looks like this: ( from expr.h )

struct _FunctionEvalInfo {
	EvalPosition        pos;
	FunctionDefinition *func_def;
};

EvalPosition - Position at which the function is being evaluated. 

NB. Unless you are doing something _extremely_ clever the correct way to
return an error is thus:

if (foo && bar)
        return value_new_error (&ei->pos, _("My error"));

FunctionDefinition - This returns a handle to a magic structure that
                     should not be used. In future this will allow for
                     extra function attributes to be set. This is normally
		     ignored, but some functions might want to access this
		     (for example, a single function callback might implement
		     various functions and can figure how it was invoked by
		     poking at this value).

Here is the actual implementation:

static Value *
add2numbers (FunctionEvalPos *ei, Value *argv [])
{
	Value *result;
	float number_a, number_b;

	switch (argv [0].type){
	case VALUE_INTEGER:
		number_a = argv [0].v.v_int;
		break;

	case VALUE_FLOAT:
		number_a = argv [0].v.v_float;
		break;

	default:
	        return value_new_error (&ei->pos, "Invalid argument type");
	}

	switch (argv [1].type){
	case VALUE_INTEGER:
		number_b = argv [1].v.v_int;
		break;

	case VALUE_FLOAT:
		number_b = argv [1].v.v_float;
		break;

	default:
	        return value_new_error (&ei->pos, "Invalid argument type");
	}

	result = value_float (number_a + number_b);

	return result;
}

Note that although typechecking is done in upper layers, both INTEGER
and FLOAT types are passed through the 'f' function declaration token.
It is of course good practice to provide a sensible default: case.

If there is an error during the function processing, an error value
should be returned.  The error string is managed by gnumeric in the
same reference counted fashion that all other strings are handled.

Return values and Strings.
--------------------------

There are a several utility functions provided by Gnumeric to create 
Values: value_new_float, value_new_int, value_new_bool, value_new_string,
value_new_cellrange, value_new_array, value_new_error. [ the nomenclature
is as of different constructors for the same class: Value ]. All Values
should idealy be created with these routines.


Functions with some optional arguments
--------------------------------------

Gnumeric support three types of functions:

* Fixed number of arguments ( dealt with above )
* Optional argument functions and
* Variable argument functions.

The difference between the latter two is that eg. the SUM ()
function will take an indefinate amount of arguments, whereas the
ERF function is best described ERF(lower limit[,upper_limit]).
In this instance, it is not worth the complexity of manualy
traversing the raw expression tree as in SUM. Instead using the
standard type checking the argument can be specified as optional,
and if unspecified a NULL will be passed in the appropriate
argv[] entry. Hence for erf (fn-eng.c):

	function_add_args  (cat, "erf",         "f|f",  "lower,upper",
		           &help_erf, gnumeric_erf );

	Note "f|f" specifying  'upper' is optional. Any arguments that
appear after a '|' symbol in the function definition string are
deemed optional.

	Hence in the code, it is vital to check optional pointers
before assuming they are valid hence:

static Value *
gnumeric_erf (FunctionEvalInfo *ei, Value **argv)
{
	float_t ans, lower, upper=0.0 ;

	lower = value_get_as_double(argv[0]) ;
	if (argv[1])
		upper = value_get_as_double(argv[1]) ;
...
}

	Note there must be a default value for optional arguments.
Here upper has a default value of 0.0.


Functions with a variable number of arguments
---------------------------------------------

In this case, the function writer is presented with the raw expression
tree data as a list, to traverse himself.

If you want to implement a function that takes a variable number of
arguments you have to change your FunctionDefinition:  instead of
setting the fifth parameter as NULL and the sixth pointing to your
function, you should set the sixth parameter to NULL and you fill in
the fifth parameter, like this:

char *help_factadd = "Adds all of its arguments";

	function_add_nodes (cat, "addall", "", "...", &help_addall,
			    addall);

NB. _add_nodes not _add_all.

We will now implement addall, a function that takes any number and
kind of arguments: strings, integers, floating point numbers, arrays
and cell ranges.  

This is a simple usage of addall, lets imagine cell A1 contains the
value 5 and the cell A2 contains the value 3.

addall (1)			returns 1
addall (1,2)			returns 3
addall (a1:a3)			returns 8
addall (a1:a3,1)		returns 9

Here is the function signature for addall:

Value *
addall (FunctionEvalInfo *ei, GList *expr_node_list)

As usual, this routine would return a newly allocated Value or a COPY of
an error if one is found.

The first argument points to the usual EvalInfo structure, and the second
argument is a linked list (a GList) that contains (ExprTree *) nodes.

The ei->pos structure can be used to determine position ( as normal )

struct _EvalPosition {
	Sheet *sheet;
	int    eval_col;
	int    eval_row;
};

'sheet' points to the sheet in which the function is being evaluated.
The 'eval_col' and 'eval_row' parameters are used to inform the function
in which context the expression should be evaluated (ie, from which
cell it is being invoked) and it is used when invoking the expr_eval
function.


Function Tokens
---------------

With intersection and iteration support
    b : boolean		identical to f, Do we need this ?
    f : float 		no errors, string conversion attempted, blanks
			converted to int 0
    s : string		no errors, blanks accepted and passed as empty
    S : scalar		any scalar non-error, blanks passed as empty
    E : scalar		any scalar non blank value
    B : scalar		any scalar even a blank

Without intersection or iteration support
    r : cell range	content is _NOT_ guaranteed to have been evaluated yet
    A : area		array, range, (range as above), or scalar
    a : array
    ? : anything