Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / HelpSource / Guides / WritingPrimitives.schelp
blob5738d071f4900d40fbc11eec6565bf13d7332d4d
1 title:: Writing Primitives
2 summary:: Writing Primitives
3 categories:: Internals
5 section:: Example
6 subsection:: SuperCollider code
8 code::
9 Cocoa {
10     prGetPathsDialog { arg returnSlot;
11         _Cocoa_GetPathsDialog
12         ^this.primitiveFailed
13     }
17 subsection:: Define your primitive
19 In your primitive source code define the primitive:
21 teletype::
22 void initCocoaFilePrimitives()
24     int base, index;
26     base = nextPrimitiveIndex();
27     index = 0;
29     definePrimitive(base, index++, "_Cocoa_GetPathsDialog", prGetPathsDialog, 2, 0);
30     // further primitives can be laid in...
31     //definePrimitive(base, index++, "_Cocoa_SaveAsPlist", prSaveAsPlist, 3, 0);
36 Here is the prototype for definePrimitive:
38 teletype::
39 int definePrimitive(int base, int index, char *name, PrimitiveHandler handler, int numArgs, int varArgs);
42 The numArgs is the number of arguments that were passed into the SuperCollider method that calls the primitive, plus one
43 to include the receiver which is passed in as the first argument.
45 (TODO varArgs ...)
47 subsection:: Write your primitive
49 teletype::g->sp:: is the top of the stack and is the last argument pushed.
50 teletype::g->sp - inNumArgsPushed + 1 :: is the receiver and where the result goes.
52 In this example, the numArgsPushed will be 2 (as specified in definePrimitive)
54 teletype::
55 int prGetPathsDialog(struct VMGlobals *g, int numArgsPushed)
57     if (!g->canCallOS) return errCantCallOS; //if its deferred, does this matter ?
59     PyrSlot *receiver = g->sp - 1; // an instance of Cocoa
60     PyrSlot *array = g->sp; // an array
62     // ...  the body
64     return errNone;
68 This example does not set the receiver, so the primitive returns the original receiver unchanged (still an instance of
69 Cocoa). Or set the object at teletype::receiver:: which again is at teletype::(g->sp - numArgsPushed + 1)::.
72 section:: Guidelines
74 subsection:: GC safety
76 If possible, you should avoid creating objects in a primitive. Primitives are much simpler to write and debug if you pass in an object that you create in SC code and fill in its slots in the primitive.
78 When you do fill in slots in an object with other objects, you must call teletype::g->gc->GCWrite(object, other_object):: in order to notify the garbage collector that you have modified a slot that it may have already scanned.
80 If you create more than one object in a primitive you must make sure that all the previously created objects are reachable before you allocate another. In other words you must store them on the stack or in another object's slots before creating another. Creating objects can call the garbage collector and if you have not made your objects reachable, they can get collected out from under you.
82 note::
83 To summarize, before calling any function that might allocate (like teletype::newPyr*::) you strong::must:: make sure these critera are fulfilled:
84 numberedlist::
85 ## All objects previously created must be reachable, which means they must exist
86     list::
87     ## on the teletype::g->sp:: stack
88     ## or, in a lang-side class/instance variable
89     ## or, in a slot of another object that fulfils these criteria.
90     ::
91 ## If any object ( teletype::child:: ) was put inside a slot of another object ( teletype::parent:: ), you must have
92     list::
93     ## called teletype::g->gc->GCWrite(parent, child):: afterwards
94     ## and, set teletype::parent->size:: to the correct value
95     ::
99 Here's an example of how it may look:
100 teletype::
101 int prMyPrimitive(struct VMGlobals* g, int numArgsPushed)
103     PyrSlot* arg = g->sp;
104     float number;
105     int err;
107     err = slotFloatVal(arg, &number); // get one float argument
108     if(err) return err;
110     PyrObject *array = newPyrArray(g->gc, 2, 0, true);
111     array->size = 0;
112     SetObject(arg, array); // return value
114     // NOTE: array is now reachable on the stack, since arg refers to g->sp
116     PyrObject *str1 = newPyrString(g->gc, "Hello", 0, true);
117     SetObject(array->slots, str1);
118     array->size++;
119     g->gc->GCWrite(array, str1);
121     // NOTE: str1 is now reachable in array, which is reachable on the stack
123     SetFloat(array->slots+1, number);
124     array->size++;
125     // A float is not an allocated object, so no need for anything special here
127     return errNone;
130 If we would have put teletype::SetObject(arg, array);:: at the end of this function, teletype::array:: would strong::not:: have been reachable at the call to teletype::newPyrString::, thus breaking the rules and introducing bugs that sooner or later would crash SuperCollider (but most probably not in the faulty code but somewhere else, making it very hard to find!)
132 warning::Do not store pointers to PyrObjects in C/C++ variables unless you can absolutely guarantee that they cannot be garbage
133 collected. For example the File and SCWindow classes do this by storing the objects in an array in a classvar. The
134 object has to stay in that array until no C object refers to it.
135 strong::Failing to observe the above two points can result in very hard to find bugs.::
138 subsection:: Type safety
139 Since SC is dynamically typed, you cannot rely on any of the arguments being of the class you expect. You should check every argument to make sure it is the correct type.
141 One way to do this is by using teletype::isKindOfSlot::. If you just want a numeric value, you can use teletype::slotIntVal::, teletype::slotFloatVal::, or teletype::slotDoubleVal:: which will return an error if the value is not a numeric type. Similarly there is teletype::slotStringVal::.
143 It is safe to assume that the receiver will be of the correct type because this is ensured by the method dispatch mechanism.
145 section:: FAQ
147 definitionList::
148 ## Now where do i put the thing to return it?
149 || into teletype::g->sp - inNumArgsPushed + 1 :: (In most primitives this is referred to by the variable teletype::a::).