Hi I played a bit with my problem of sporking a member function and found that the attached example (static_member.ck) actually *do* work. However (as the comment in the code suggest) if the function sporkee() is *not* declared as a static function, I get: [chuck](VM): NullPointerException: shred[id=2:spork~exp], PC=[3] Could soneone provide a meaningfull explanation, why that is? And is the exception (when "static" is removed) to be considered a bug in chuck? Finally, what *exactly* does "static" mean? The documentation states that static data and functions "are shared by all instances of that class". Ok, I understand that. But apparently "static" doesn't mean "cannot be changed" (which is what the word implies), mostly applicable to data, as the other example (static.ck) shows. So, why was the word "static" chosen, shouldn't it have been "global" or "shared" or something like that? Or is there a historical/traditional reason behind the word "static" that I'm not aware of? Whatever the reason, I think this (that "static" doesn't mean "unchangeable" and when + why it's neccessary to declare things, esp. functions "static", like in the case of "spork_member.ck") needs mention in the manual, since I might not be the only one thrown off by this... Thanks in advance for any response. -- peace, love & harmony Atte http://www.atte.dk | quartet: http://www.anagrammer.dk http://www.atte.dk/gps | compositions: http://www.atte.dk/compositions class Test { fun static void sporkee() { // must be static, otherwise gives: // [chuck](VM): NullPointerException: shred[id=2:spork~exp], PC=[3] while(true) { <<<"i'm sporkee">>>; 1::second => now; } } fun void sporker() { spork ~ sporkee(); } } Test test; test.sporker(); // keep alive while(true) { 1::second => now; } class Test { 0 => static int x; 0 => int y; fun void tell_x() { <<<x>>>; } fun void tell_y() { <<<y>>>; } } Test test; test.tell_x(); test.tell_y(); 1 => test.x; 2 => test.y; test.tell_x(); test.tell_y();
Atte, I know that sporking member functions is on one of the todo lists, so its safe to assume that that functionality isnt working completely at the moment. There are plenty of good reasons to want to spork a non- static member function though, so it should be on its way. One quick hack to simulate sporking a member function could work like this: class Test { void sporkee() { //do something } } fun void spork_helper( Test t ) { t.sporkee(); } Test u; spork ~ spork_helper( u ); "static" originated in C, but it is used in ChucK the same way that C+ + and Java uses it. Under the hood, whats happening is that there is a single address somewhere that holds the value of a static member variable. Each time you call Class.static_data or Instance.static_data, its referencing the same underlying memory for a given class and variable name. The name makes more sense in the context of C++, because compilers would statically allocate the memory used to hold static variables when you compiled the program. In ChucK, runtime and compile time are basically the same, so this distinction isnt as significant, but the name has stuck around. my non-formal definition of static: the data represented by a static variable is the same for every instance of a class at a given point in time. Changes to this data in one instance are immediately reflected in other classes. Static functions are basically just like regular, non-class functions, except that they exist within the namespace of a class. "const" (constant) is the keyword used to specify immutable data in Java and C++. I dont think its currently used in ChucK, but its on the reserved word list, so I imagine that it will be implemented at some point. Often times it is desired for static data to be constant also, but not always. spencer On Jun 19, 2006, at 7:26 PM, Atte André Jensen wrote:
Spencer Salazar wrote:
That's good to know. What I need is to spork a member function from within another member function. And contrary to what my simple example suggest, there is indeed something fishy here. At least I can't make my semi complicated example (attached) work. It's supposed to listen for 5 taps on the a midikeyboard, and on the fifth, stop all signalling shreds, update the tempo and restart the signalling shreds. As the code is attached i get [chuck](VM): NullPointerException: shred[id=2:spork~exp], PC=[3] If I try to do as the working mini example attached in the first mail (declaring sendQuarter static) chuck seg faults. I hope this works more convincingly in the upcomming release. -- peace, love & harmony Atte http://www.atte.dk | quartet: http://www.anagrammer.dk http://www.atte.dk/gps | compositions: http://www.atte.dk/compositions public class Time { 60 => float bpm; static dur length_quarter; static dur length_eight; static dur length_sixteen; 1::minute/bpm => length_quarter; length_quarter/2 => length_eight; length_quarter/4 => length_sixteen; static Event quarter; static Event eight; static Event sixteen; static int ids[10]; 1 => int midi_dev; MidiIn midi_in; MidiMsg midi_msg; 36 => int listen_key; if (!midi_in.open(midi_dev)) me.exit(); Shakers inst => dac; fun static void start() { spork ~ sendQuarter(); } fun void stop() { for(0 => int i; i < ids.cap(); i++) if(ids[i] != 0) { machine.remove(ids[i]); 0 => ids[i]; } } fun void addId(int id) { for(0 => int i; i < ids.cap(); i++) if(ids[i] == 0) { id => ids[i]; ids.cap() => i; } } fun void sendQuarter() { addId(me.id()); while(true){ inst.noteOn( 0.5 ); quarter.broadcast(); length_quarter => now; } } fun void sendEight() { while(true){ quarter.broadcast(); length_eight => now; } } fun void sendSixteen() { while(true){ sixteen.broadcast(); length_sixteen => now; } } fun void update_from_quarter(dur quarter) { quarter => length_quarter; 1::minute/length_quarter => bpm; length_quarter/2 => length_eight; length_quarter/4 => length_sixteen; } fun void listen() { 2::second => dur timeout; now => time first_tap; now => time latest_tap; 0 => int tap_count; 4 => int nb_taps; while(true){ midi_in => now; while(midi_in.recv(midi_msg) ) { // listen for noteon (144) on listen_key if(midi_msg.data1 == 144 && //midi_msg.data2 == listen_key && midi_msg.data3 != 0) { if(now - latest_tap < timeout) { 1 +=> tap_count; if(tap_count == 1) { //now => first_tap; } else if(tap_count == nb_taps ) { stop(); update_from_quarter((now - first_tap)/nb_taps); start(); now => first_tap; 0 => tap_count; } } else { now => first_tap; 0 => tap_count; } now => latest_tap; } } } } } Time T; T.start(); //spork ~ T.sendQuarter(); //spork ~ T.sendEight(); //spork ~ T.sendSixteen(); //spork ~ T.listen(); //2::second => now; //T.stop(); // keep alive while(true){ 100::minute => now; }
Atte, Even given proper handling of sporked member functions, there is a problem with your code below. Essentially, you are calling a non- static member function from a static function, without an instance of the class. Specifically, when you call T.start(), start() "loses" its association with T. Thus, when you call sendQuarter() from within start(), even without the spork, youd get a NullPointerException, because sendQuarter(), a non-static function is being called without an associated instance. Essentially, within a static function, you cant access non-static member variables or functions unless they are associated with an explicit instance of the class--ie you need InstanceName.start() or InstanceName.data, not just start() or data. An important side-effect of this is that Test.start() and T.start() will behave equivalently. This is one of the more subtle points about static member functions; it would probably help to mention this in the docs, and also I think it should be a compile error. But yes, even with that issue resolved, your code wouldnt work because of the sporking member functions issue. Since static member functions seem to work with spork, have you considered making all of the members of Time static? Although thats probably undesirable in the long run (you couldnt run multiple different tempos within the same chuck virtual machine), it should operate well enough if you only need a single master tempo. Would that work until the current sporking behavior is cleaned up? spencer On Jun 20, 2006, at 4:15 AM, Atte André Jensen wrote:
Spencer Salazar wrote:
Thanks alot! That was exactly the kind of answer I was hoping to get. It all makes much more sense now. In retrospect, I think I should have seen it my self, sbut then again...
It should very much be in the doc. Subtle sometimes means "you never figure out yourself why this special case doesn't work"...
I had even tried, but must have missed something. In the mean time I made it work by moving most functions outside the class (for proof of concept + maximum clutter), but after reading this I managed to rearrange everything back in the class.
I wouldn't say more tempos wouldn't have any use, but I find it hard to see. Basically what I have is four sporked functions each sending out messages of my own Event subclass "timeEvent" (should be named more approppriate) that are defined like this: static timeEvent measure; static timeEvent quarter; static timeEvent eight; static timeEvent sixteen; Each message is tagged with whics measure in the period, which quarternote in the measure, which eightnote in the quarter note and which sixteennote in the eightnote they are. Printing out this gives (snipped, with sixteennote triplets): 0 : 0 : 0 : 0 0 : 0 : 0 : 1 0 : 0 : 0 : 2 0 : 0 : 1 : 0 0 : 0 : 1 : 1 0 : 0 : 1 : 2 0 : 1 : 0 : 0 0 : 1 : 0 : 1 0 : 1 : 0 : 2 0 : 1 : 1 : 0 0 : 1 : 1 : 1 0 : 1 : 1 : 2 0 : 2 : 0 : 0 0 : 2 : 0 : 1 0 : 2 : 0 : 2 0 : 2 : 1 : 0 0 : 2 : 1 : 1 0 : 2 : 1 : 2 0 : 3 : 0 : 0 0 : 3 : 0 : 1 This is printed from a listener, listening for the sixteen note stream. This all means: 1) When you sit somewhere you can easily place something on say 4 in every other measure, simply listen to the quarternote-stream and react when you woke up by the last quarter in an even/uneven bar. If you really listen for the last beat and not the beat tagged 4 the listener would even execute meaningfully and 100% synced in 3/4. 2) I have set up a basic midi-keyboard-remote-control. Tapping on the lowest C five times (1-2-3-4-1) will set the tempo to what was tapped + restart the senders on the final 1. This way I hope to be able to syncronice to live drummers and be able to adjust to them, should they loose sync with chuck. Also simply tapping on the lowest C# will restart the senders without changing the tempo. I tested it (with Janet Jackson's "Rhythm Nation" :-)) and it's possible, and the CD is listening even less than (most) drummers...
Would that work until the current sporking behavior is cleaned up?
I think it would work regardless. This setup doesn't really make sense if two or more senders are spitting out say quarter-note events at different rates. -- peace, love & harmony Atte http://www.atte.dk | quartet: http://www.anagrammer.dk http://www.atte.dk/gps | compositions: http://www.atte.dk/compositions
participants (2)
-
Atte André Jensen
-
Spencer Salazar