Previous Thread
Next Thread
Print Thread
Rate This Thread
Hop To
#4521721 - 05/21/20 07:13 AM TARGET - DeferCall help needed  
Joined: Mar 2020
Posts: 12
dmonds Offline
Junior Member
dmonds  Offline
Junior Member

Joined: Mar 2020
Posts: 12
Auckland, New Zealand
Hi All,

By default DeferCall() requires both the alias for the function and one argument.

Is there a way to modify target.tmh to accept functions with 0 arguments?
How about functions which require 2 arguments?

The first one is easy enough, for me to define my function with a dummy argument.
However, what I'm really after is the second one.

I have created several functions which require 2 arguments but cannot use DeferCall() with them at present (but want to).

Any thoughts?

Thanks
dmonds

Inline advert (2nd and 3rd post)

#4521722 - 05/21/20 07:16 AM Re: TARGET - DeferCall help needed [Re: dmonds]  
Joined: Mar 2020
Posts: 12
dmonds Offline
Junior Member
dmonds  Offline
Junior Member

Joined: Mar 2020
Posts: 12
Auckland, New Zealand
...my second question is...

If I use DeferCall() with a delay of, say 120 seconds, is there a way to cancel this prior to the 120 second timeout?

Before you ask, no, I do not want to be forced to use REXEC.

Many thanks for any ideas or suggestions you can offer.

dmonds

#4521728 - 05/21/20 10:41 AM Re: TARGET - DeferCall help needed [Re: dmonds]  
Joined: Jul 2016
Posts: 61
Drakoz Offline
Junior Member
Drakoz  Offline
Junior Member

Joined: Jul 2016
Posts: 61
Originally Posted by dmonds
Is there a way to modify target.tmh to accept functions with 0 arguments?

The first one is easy enough, for me to define my function with a dummy argument.


Yes, that is the best solution. Just put a 0 for the "param" in DeferCall and ignore the parameter in your function. There is little benefit to trying to do otherwise.

Code

...DeferCall(1000, &MyFunc, 0)...

int MyFunc(int x) 
{
     // your code here, just ignire x
}


Quote

How about functions which require 2 arguments?


To do this, pack your parameters into a single variable, or an array and pass an alias to the array. You would then unpack the array or variable as needed in the function.


Using a packed variable...

Here is an example packing several numbers into a single int. This technique is used throughout TARGET. When you do PULSE+'a', you are packing the value for PULSE and the ASCII code for the letter 'a' into single integer variable.

Code
From target.tmh...
define	PULSE	0x01000000  // a hex value

And 'a' = 0x61 hex, or 97 decimal.  

Hence PULSE+'a' = 0x01000061

Here is a code example....

//packedvar is an int that stores several values bit shifted
packedvar = 0x1211      // This has two params, 12 hex and 11 hex, packed as a hex value of 0x1211.

...DeferCall(1000, &MyFunc, packedvar)...

int MyFunc(int parms)
{
    x = parms & 0xff     // gets lower parameter = 0x11
    y = parms >> 8       // gets upper parameter = 0x12
}


The example above is using all normal C syntax.

x = parm & 0xff is a mask. ANDing (with the & character) 0xff with parm results in 0x0011 becasue we have masked off the upper 8 bits of 0x1211.

parm >> 8 bit shifts the value stored in parm right by 8 bits, which basically shifts the 0x01 off the right side (it is lost) and we are left with 0x02.



Or, using an array...

With an array, you can send as many parameters as you want through DeferCall() because you will pass the array's memory address (or its alias using TARGET nomemclature). The array must be defined globally. Otherwise, it will not exist when DeferCall tries to pass it to your function.

Code
// defined globally (outside of main() )
int param1[3] = {10, 11, 12};   // integer array of 3 values
int param2[3]                   // same, but not preset. Either array type is fine

main()
{
    // We will use param2 for this example
    param2[0] = 10;
    param2[1] = 11;
    param2[2] = 12;

    ... DeferCall(1000, &MyFunc, &param2)....
}

int MyFunc(alias params)
{
    x = params[0];      // x = 10    (param2[0])
    y = params[1];      // y = 11    (param2[1])
    z = params[2];      // z = 12    (param2[2])
}



You can also use a struct, or even an array of struct. For example:

Code
// must define the structure and struct variables globally
struct FuncParameters {
    int val1;
    int val2;
}

FuncParameters myParms;

main {
    myParms.val1 = 11;
    myParms.val2 = 12;

    ... DeferCall(1000, &MyFunc, &myParms)....
}

int MyFunc(alias parms)
{
    x = parms.val1;     // x = 11
    y = parms.val2;     // y = 12
}


The key to all this is, DeferCall() can only store one parameter, and that parameter must be a number in fact. But since a number can refer to a variable's address (an alias), we can pass an alias to any kind of variable we want, including complex variables like strings, arrays, structures, or arrays of structures.

I did not test the above examples, so if you try it and it isn't working, I might have made a mistake. But the concept is correct.

#4521733 - 05/21/20 11:10 AM Re: TARGET - DeferCall help needed [Re: dmonds]  
Joined: Jul 2016
Posts: 61
Drakoz Offline
Junior Member
Drakoz  Offline
Junior Member

Joined: Jul 2016
Posts: 61
Originally Posted by dmonds
...my second question is...

If I use DeferCall() with a delay of, say 120 seconds, is there a way to cancel this prior to the 120 second timeout?

Before you ask, no, I do not want to be forced to use REXEC.


See RemoveEvent() in hid.tmh as well as tempoproc() in target.tmh for an example of how to use it. Also, look at the definition for DeferCall() in target.tmh as well as AutoRepeat(), StopAutoRepeat(), and rexecproc() in target.tmh. rexecproc() is the function that actually does the REXEC(). All of them use a handle in some form or another which of course we understand as the handle used by REXEC(). It is important to understand that handle is actually used by PostEvent() as EV_USR+1+handle. Knowing that, you can post an event using PostEvent() in order to create a DeferCall(), and then cancel that event using RemoveEvent().

Here is the definition of DeferCall() in target.tmh (I have edited it a little for readability).

Code
int DeferCall(int delay, alias proc, int param)
{ 
     PostEvent(EV_USR, &proc, param, delay); 
}


All DeferCall() does is add a call type (EV_USR) to PostEvent(). EV_USR is a handle, like used in REXEC. So you could call PostEvent as follows:

PostEvent(EV_USR+1+handle, &MyFunc, params);

And then cancel &MyFunc using:

RemoveEvent(EV_USR+1+handle);

You could probably cancel a DeferCall() function with RemoveEvent(EV_USR), but I am not sure what this will do. It may remove all currently scheduled events? Or just all DeferCalls??? So I would suggest you use the EV_USR+1+handle to create a handle like I explain above, where handle is a number just like you would use for REXEC.

I notice that TEMPO uses handles from EV_USR+100 and above as demonstrated in tempoproc() in target.tmh. So looks like it is best to make usre all handles are < 100. For example EV_USR+1+handle, where handle should be < 100.

I haven't tested this, but it seems pretty clear this is how TARGET is using these functions.

Oh, and it is probably pushing the limit to create an Event and Cancel it within 120ms. TARGET may not be able to create and cancel an even that fast unless your code that creates it is tightly coupled with the code that cancels it.

Last edited by Drakoz; 05/21/20 11:13 AM.
#4521868 - 05/22/20 07:16 AM Re: TARGET - DeferCall help needed [Re: Drakoz]  
Joined: Mar 2020
Posts: 12
dmonds Offline
Junior Member
dmonds  Offline
Junior Member

Joined: Mar 2020
Posts: 12
Auckland, New Zealand
Re answer for 1...Brilliant!

For numbers.

You say "DeferCall() can only store one parameter, and that parameter must be a number in fact" <--- nope, works if I parse a string as well.

My issue now is that the functions I'm calling that have two arguments typically have one number and one string.
That's fine, using a corollary of your theory...I can just merge the integer into the string using sprintf() then diverge in the function.

My only gripe with this is that these functions are not just called via a DeferCall but many times as straight calls.....at least it gives me a solution.

Thanks
dmonds

Last edited by dmonds; 05/22/20 07:17 AM.
#4521869 - 05/22/20 07:21 AM Re: TARGET - DeferCall help needed [Re: dmonds]  
Joined: Mar 2020
Posts: 12
dmonds Offline
Junior Member
dmonds  Offline
Junior Member

Joined: Mar 2020
Posts: 12
Auckland, New Zealand
Re answer for 2...Brilliant!

I will have a play with that and see if I can get it to work.

@Drakoz, you sir are a legend!

Apart from knowing this stuff , you actively help people...not just with a solution, but also with learning!

Thank You.

dmonds

#4521874 - 05/22/20 08:36 AM Re: TARGET - DeferCall help needed [Re: dmonds]  
Joined: Mar 2020
Posts: 12
dmonds Offline
Junior Member
dmonds  Offline
Junior Member

Joined: Mar 2020
Posts: 12
Auckland, New Zealand
EDIT: I just tested this in my script and my assumption is correct....which solves my DeferCall "problem" above regarding 2 parameters!!!

Off topic perhaps...question about defining functions with "optional" arguments...

When I define a function;

int fnSomeFunction(int x, int y=0) {}

Assuming that when I call the function via 'fnSomeFunction(a, b);' that 'a' is assigned to the local 'x' variable in the function and that 'b' overrides '0' and is assigned to 'y'.

Can I therefore call the function via 'fnSomeFunction(a);' and assume 'a' is assigned to 'x' and 'y' simply stays at '0' and we won't get a runtime error for not parsing 'y'?

dmonds

p.s. you can see where I'm going with this, right?

Last edited by dmonds; 05/22/20 08:50 AM.
#4521950 - 05/22/20 04:43 PM Re: TARGET - DeferCall help needed [Re: dmonds]  
Joined: Jul 2016
Posts: 61
Drakoz Offline
Junior Member
Drakoz  Offline
Junior Member

Joined: Jul 2016
Posts: 61
Originally Posted by dmonds
Re answer for 1...Brilliant!


I'm glad this is useful.

Quote
For numbers.

You say "DeferCall() can only store one parameter, and that parameter must be a number in fact" <--- nope, works if I parse a string as well.


Yes, and a string is always passed as an alias. What is an alias in reality? For a string, it is the memory address of the variable which points to the string, which is a number. For an array (say an array of int), it is the memory address of the variable that points to the array. Or to say it more completely, if a = "this is my string", &a is the address of the variable a (an alias), and &&a is the address to the memory location where the string is actually stored.

So that is what I meant. This is actually critical to how TARGET works because all the button mapping, EXEC, REXEC, or function calls we store using MapKey(), KeyAxis(), etc. are all stored as integers in a single large array which is called keyalloc[] which is a huge buffer of all this data. Look for keyalloc[16384] in target.tmh for its definition. You don't need to know this to do the stuff I described above, but it helps when trying to answer questions like what you asked.

Quote
My issue now is that the functions I'm calling that have two arguments typically have one number and one string.
That's fine, using a corollary of your theory...I can just merge the integer into the string using sprintf() then diverge in the function.


Yes, that sounds like a good technique.

The more advanced (better) way to do it is use a struct like I showed in my last example.

Here is kind of the concept, but I am pretty sure this is wrong in a few critical areas because dealing with strings in TARGET is something I still have to think about to get it to work with examples like this. See notes below for those areas I am unsure of.

Code
// must define the structure and struct variables globally
// In C (and TARGET), struct creates a special variable type (like int, float, etc.)
struct FuncParameters {
    int myint;
    alias mystring;
}

// So we use it like we would use int varname
// Here, we define a variable myParms of variable type FuncParameters
// We access myParms as myParms.myint and myParms.mystring.
FuncParameters myParms; 

main {
    myParms.myint = 11;

    // I'm not sure the following works. Might have to use Map() to do this (which is an advanced topic). 
    // Look at examles of Map() in target.tmh for details.
    &myParms.mystring = "this is my string";   // or use what ever method you need for creating a string like sprintf()

    // Or might have to do it like this...
    alias s;
    &s = "this is my string";     // again use what ever method for creating the string s
    myParms.mystring = s;     // now we put it in the struct with a simple assignment
    // Sorry, I don't have time to play with this more right now, but try it and ask questions. 

    ... DeferCall(1000, &MyFunc, &myParms)....
}

int MyFunc(alias parms)
{
    int x;
    alias y;

   x = parms.myint;                 // x = 11
   &y = &parms.mystring;     // y = "this is my string"
}


Again, I didn't test this, so I probably have some mistakes (like how I defined the strings), but hopefully the concept makes sense even if we will have to play with it to figure it out. For example, it might be necessary to use Map() to make the x and y assignments I did above. See examples using Map() in target.tmh, or try it and ask questions.

Quote
My only gripe with this is that these functions are not just called via a DeferCall but many times as straight calls.....at least it gives me a solution.


True, which is why using a struct is the better way to do it. It is easier to pack and unpack things using a struct since the struct defines the packing for you.

For more examples of this in TARGET, see the sAxis and sDevice structures in target.tmh which are used to define axdata and devicedata. These variable structs, along with keyalloc[] is where all the magic in TARGET works. Note how these are used in various functions using Map() to access the lower levels of the structures. But I have to apologize as I am pretty sure my example above will not work. I will have to look at this in more detail later.

#4521951 - 05/22/20 04:50 PM Re: TARGET - DeferCall help needed [Re: dmonds]  
Joined: Jul 2016
Posts: 61
Drakoz Offline
Junior Member
Drakoz  Offline
Junior Member

Joined: Jul 2016
Posts: 61
Originally Posted by dmonds
Re answer for 2...Brilliant!
@Drakoz, you sir are a legend!

Apart from knowing this stuff , you actively help people...not just with a solution, but also with learning!


My ulterior motive is, by helping others I learn how to do it myself. :-) Teaching is the best way to learn because it forces you to understand something really well in order to tell someone else how to do it. So it is questions like yours that helped me figure this out all out.

#4521954 - 05/22/20 04:57 PM Re: TARGET - DeferCall help needed [Re: dmonds]  
Joined: Jul 2016
Posts: 61
Drakoz Offline
Junior Member
Drakoz  Offline
Junior Member

Joined: Jul 2016
Posts: 61
Originally Posted by dmonds
When I define a function;

int fnSomeFunction(int x, int y=0) {}

Assuming that when I call the function via 'fnSomeFunction(a, b);' that 'a' is assigned to the local 'x' variable in the function and that 'b' overrides '0' and is assigned to 'y'.

Can I therefore call the function via 'fnSomeFunction(a);' and assume 'a' is assigned to 'x' and 'y' simply stays at '0' and we won't get a runtime error for not parsing 'y'?


Yes, as you figured out, this is exactly how it works. There are numerous examples of this in target.tmh. TARGET depends on this massively to define variables that the user has not given, or even to define variables that they didn't even document for common functions. For example, look at ActKey() which is defined as follows:

Code
int ActKey(int k, int x=0x7fffffff)


There is nothing in the TARGET documentation about the 2nd variable, x, but they use the x variable regularly inside target.tmh to control how ActKey() works even though such usage is not explained for the user.

#4522377 - 05/25/20 11:18 PM Re: TARGET - DeferCall help needed [Re: dmonds]  
Joined: Mar 2020
Posts: 12
dmonds Offline
Junior Member
dmonds  Offline
Junior Member

Joined: Mar 2020
Posts: 12
Auckland, New Zealand
Interesting bug in TARGET I just found by accident.

I have a function with two string arguments.
I often use 'int' as a string variable type.
I was mindlessly refactoring my functions so that any optional arguments have a default value and instead of using '0' I used "" in a function declaration.
(I know better now!)

Declaring 'fnFunction(int x, int y="") {}' results in a compile error...no console error message and the Thrustmaster FAST service crashes!

#4522890 - 05/29/20 05:50 AM Re: TARGET - DeferCall help needed [Re: dmonds]  
Joined: Mar 2020
Posts: 12
dmonds Offline
Junior Member
dmonds  Offline
Junior Member

Joined: Mar 2020
Posts: 12
Auckland, New Zealand
Actually @Drakoz, you have given me a thought about another feature of my script.

Two thoughts actually...

1) In Elite Dangerous, the game updates a single line 'status.json' file.
This file can have between 3 - 17 key value pairs.
As I know the key names, I have already written the code to be able to extract the values by name.
The code works, but I consider it somewhat ugly.
Perhaps I could use "struct" to more easily extract the key values?

sample status.json
Code
{ "timestamp":"2020-02-06T02:09:41Z", "event":"Status", "Flags":2217263368, "Pips":[4,4,4], "FireGroup":0, "GuiFocus":0, "Fuel":{ "FuelMain":0.000000, "FuelReservoir":0.437468 }, "Cargo":0.000000, "LegalState":"Clean", "Latitude":-39.155632, "Longitude":-165.124420, "Heading":258, "Altitude":0, "BodyName":"HIP 36601 C 1 a", "PlanetRadius":761855.125000 }


2) Next...I recently wrote a function pair to write/read some variables I want to be persistent across script/game restarts.
Again, the code is a bit meh, but works.
Using struct may be something I could use to better organise and manage this?

Here's a sample of the code I use....

Code
define SAVE 0
define LOAD 1
alias MyStatusFile = "d:\\Thrustmaster\\ED_TargetScript\\MyStatusFile.json";

int VoiceVolume = 75;  // this can be adjusted on the fly within my script for a voice feedback function I have
int ShipDismissed = 0; // this is just a flag variable I use to track the state of when the ship I'm flying has been 'dismissed' or is still local

int fnsMyStates(int action)	{

	int fp;														// General File Pointer Variable 
	int fpresult;
	int maxbuf = 64;
	int buf;

	char dummy; Dim(&dummy, maxbuf); dummy[0] = 0;
	char GetVar; Dim(&GetVar, 8);  GetVar[0] = 0;				// Temp variable to extract out individual vars
	
	sprintf(&dummy, "{ \"VoiceVolume\":%03d, \"ShipDismissed\":%d }", VoiceVolume, ShipDismissed);	// add leading zeros to VoiceVolume so static length = 3 chars

	buf = strlen(&dummy);
	strdel(&dummy, buf+1, maxbuf);

	char MyVars; Dim(&MyVars, buf+1); MyVars[0] = 0;

	if (action == SAVE)	{

		strcat(&MyVars, &dummy);
		
		fp = fopen(&MyStatusFile, "w");							// creates the file if it doesn't exist
		
		if (fp) {
			fpresult = fwrite(&MyVars, 1, buf, fp);				// Write MyStates to file
			fclose(fp);
		}
		else {
			printf("Error opening file to write: %s\x0a", &MyStatusFile);
		}		
	}
	
	else if (action == LOAD) {

		fp = fopen(&MyStatusFile, "r");							// open file to read
		
		if (fp) {
			fpresult = fread(&MyVars, 1, buf, fp);				// Load MyStates from file
			fclose(fp);	
			strdel(&MyVars, buf, maxbuf);						// Garbage removal
			
			if (fpresult > 0) {									// Read from file was successful
				strsub(&GetVar, &MyVars, 16, 18);
				VoiceVolume = ieval(&GetVar);					// Extract VoiceVolume
				
				strsub(&GetVar, &MyVars, 37, 38);
				ShipDismissed = ieval(&GetVar);					// Extract ShipDismissed
				
				printf("\x0a\MyStates successfully loaded\x0a");
			}
		}
		else {
			printf("\x0a\!!! Non-Fatal Error opening file to read: %s\x0a", &MyStatusFile);
		}		
	}
	else {
		printf("Incorrect parameter: fnsMyStates Action = %d\x0a", action);
	}
}



So basically, I'm asking, would 'struct' be a better more elegant solution?

I know I could spend hours and hours wrapping my head around 'struct' then refactoring my code to suit, so hoping to get a sanity check before potentially wasting my weekend.

Cheers
dmonds

Last edited by dmonds; 05/29/20 05:54 AM.

Moderated by  RacerGT 

Quick Search
Recent Articles
Support SimHQ

If you shop on Amazon use this Amazon link to support SimHQ
.
Social


Recent Topics
CD WOFF
by Britisheh. 03/28/24 08:05 PM
Carnival Cruise Ship Fire....... Again
by F4UDash4. 03/26/24 05:58 PM
Baltimore Bridge Collapse
by F4UDash4. 03/26/24 05:51 PM
The Oldest WWII Veterans
by F4UDash4. 03/24/24 09:21 PM
They got fired after this.
by Wigean. 03/20/24 08:19 PM
Grown ups joke time
by NoFlyBoy. 03/18/24 10:34 PM
Anyone Heard from Nimits?
by F4UDash4. 03/18/24 10:01 PM
RIP Gemini/Apollo astronaut Tom Stafford
by semmern. 03/18/24 02:14 PM
Copyright 1997-2016, SimHQ Inc. All Rights Reserved.

Powered by UBB.threads™ PHP Forum Software 7.6.0