How do i determine if a varabile exist?


#21

@bigalgator,

It is very easy setting default parameters for a function.

Let’s take the MACD example of the ADK Sample project.
It looks like this

// ExampleMACD() 
// This function demonstrates
// how to call-back internal AFL function
AmiVar VExampleMACD( int NumArgs, AmiVar *ArgsTable )
{
	// First you need to fill-in
	// arguments table.
	//
	// we will call back MACD that gets two float
	// arguments:
	
	AmiVar arg[ 2 ];
		
	arg[ 0 ].type = VAR_FLOAT;
	arg[ 0 ].val = 12;

	arg[ 1 ].type = VAR_FLOAT;
	arg[ 1 ].val = 26;

	
	// Now you can call internal AFL function
	// using CallFunction callback pointer of the site interface
	// Please note that NO ERROR checking is done
	// on number/types of arguments passed
	// Specifying wrong args here ends with access violation in most cases
    AmiVar result = gSite.CallFunction( "macd", 2, arg );

	return result;
}

And the entry of that function in the function table of the same Sample project looks like this

"ExampleMACD",		{ VExampleMACD, 0, 0, 0, 0, NULL }, 

So in simple words,
the string “ExampleMACD” is your AFL function’s name,
VExampleMACD is the DLL function’s name
1st zero is your number of array arguments
2nd zero is the number of string arguments
3rd zero is the number of float arguments
4th zero is number of defaults
and the NULL is indicating that no defaults are defined.

You have all zero here because the function has two fix variables set within the function. So in function table above no number of arguments need to be set. That’s why all zero.

Now next (modified) case would be using no fix variables but passing some arguments from AFL to your function. In that case the simplest form would look like this modification of the above example

// ExampleMACD() 
// This function demonstrates
// how to call-back internal AFL function
AmiVar VExampleMACD( int NumArgs, AmiVar *ArgsTable )
{
	// First you need to fill-in
	// arguments table.
	//
	// we will call back MACD that gets two float
	// arguments:
	
	AmiVar arg[ 2 ];// two arguments
	
	arg[ 0 ].type = VAR_FLOAT;
	arg[ 0 ].val = ArgsTable[ 0 ].val;// your 1st inserted parameter of ExampleMACD(arg1, arg2)

	arg[ 1 ].type = VAR_FLOAT;
	arg[ 1 ].val = ArgsTable[ 1 ].val;// your 2nd inserted parameter of ExampleMACD(arg1, arg2)
	
	// Now you can call internal AFL function
	// using CallFunction callback pointer of the site interface
	// Please note that NO ERROR checking is done
	// on number/types of arguments passed
	// Specifying wrong args here ends with access violation in most cases
	AmiVar result = gSite.CallFunction( "macd", 2, arg );

	return result;
}

The function table would look like this now

"ExampleMACD",		{ VExampleMACD, 0, 0, 2, 0, NULL }, 

Why “2”? Because inbuilt MACD function that we call via gSite.CallFunction expects two arguments of type number so we set “2” as number of floats at 3rd place after VExampleMACD. Still no default values here. That’s why still “0, NULL”

In the next (final) case of this post we add some default values to be used if nothing is passed.
For this case we only need to change the function table of the 2nd example’s function of this post and define two default values.

float MACD_defaults[] = {12.0f, 26.0f};// if no arguments are inserted in AFL function MACD() then these default values will be used for calculation

FunctionTag gFunctionTable[] = {																		//#array, #string, #float, #defaults, defaultlist
								"ExampleMACD( fast_default = 12, slow_default = 26 )",	{ VExampleMACD, 0, 0, 0, 2, MACD_defaults },	
								// your other functions .....
								};

So “2” is now at 4th place after VExampleMACD but not at 3rd place as before (remember, 4th place after DLL function name is number of default values). And NULL is replaced by MACD_defaults. MACD_defaults is your list of default values as you can see above of function table. That sample list consists of two values since two ones are expected as default ones in that example function (I am only repeating this for people who would read to quickly without carefully reading).

Note, I have changed the description too -> ExampleMACD( fast_default = 12, slow_default = 26 )
The stuff within the brackets will be shown in AFL editor if you type “ExampleMACD(” there and “Parameter info” being checked in Tools-Preferences-Editor.

I am still not sure if that’s what Nick was asking for or unsure about.

Please report/add if there are any mistakes/typos found in this quick tutorial.


#22

that example I gave 5 days back was maybe not the answer to the question but I encountered a similar problem that I define my parameters in the plugin but I also want the option to define or hard code them in the AFL. So the plugin needs to check if the parameter is already set in the AFL. Code below works. It calls a parameter from the AFL. If the parameter is not set in the AFL, the value is NULL it sets the parameter in the plugin.

	AmiVar ZTCellHeight = gSite.GetVariable("ZTCellHeight");
	if (ZTCellHeight.val == NULL)
	{
		arg6[0].type = VAR_STRING;
		arg6[0].string = (char *)gSite.Alloc(100);
		strcpy_s(arg6[0].string, 100, "ZT Cell Height");
		arg6[1].type = VAR_FLOAT;	arg6[1].val = 20.0f;
		arg6[2].type = VAR_FLOAT;	arg6[2].val = 5.0f;
		arg6[3].type = VAR_FLOAT;	arg6[3].val = 200.0f;
		arg6[4].type = VAR_FLOAT;	arg6[4].val = 1.0f;
		arg6[5].type = VAR_FLOAT;	arg6[5].val = 0.0f;
		ZTCellHeight = gSite.CallFunction("Param", 6, arg6);
	}

#23

Keep in mind that in C/C++ NULL is just a 0 (number zero). It is defined in the header files as:

#define NULL 0

C/C++ NULL does not have special meaning as Null (aka. Empty) in AFL1 . So the check you are doing is highly unreliable as you are just checking if variable is equal zero.

Your code only “appears” to work. If this variable happens to be zero your code would think it does not exist, while it really exists.

To really find out that variable does not exist there is documented way (see Plugin.h header file):

// the list of AmiVar types
enum { VAR_NONE, VAR_FLOAT, VAR_ARRAY, VAR_STRING, VAR_DISP };

So correct code is:

AmiVar var = gSite.GetVariable("test");
if( var.type == VAR_NONE )
{
 // does not exist
}

But as I said, DLL <-> AFL communication via variables is really BAD idea for reasons outlined in my previous replies.

1) Null in AFL is a special “magic” value, different than zero (currently -1e10, but don’t hardcode that), and it has special meaning. It means “unknown” or “empty”. It is used to mark bars where data are not available / not known, for example for the very first 19 bars of 20-bar simple moving average.


#24

thank you for the correct code.

I read your previous comments about DLL-AFL communication via variables and I agree that calling parameters/variables from the plugin could lead to problems if the parameter is not there or has been accidentally altered in the AFL.

But in the case I have shown above I have set the correct parameter in the plugin. It checks if the same parameter set in the AFL, but if it does not find it then it takes the setting from the plugin. I do not see how this can lead to a problem.


#25

It would cause problems if your plugin function was reentered simultaneously from multiple threads.
But since I know that ADK is not for professional programmers and writing re-entrant code is well above ADK user skill, each of your plugin is protected by AmiBroker from multithread re-entrancy via per-plugin critical section and that is sole reason why it works.

But as soon as you set variable in function ‘A’ and rely on given value in function ‘B’ you may be very surprised because the other thread may called your function ‘B’ and the variable you are reading belongs to other thread (each AFL formula lives in own thread and has its own private variables)

As I wrote already, DLLs should expose CLEAR and OBVIOUS interfaces to talk to the outside world (i.e. your formula). Clear and obvious interface for each function consists of two parts:

  1. input arguments
  2. return value

Having some hidden relationships and hidden communication via variables is a straight road to having severe headache in the future.


#26

ok thanks, I’ll take your word for it :slight_smile: or I mean to say that I am going to leave this effort out of my plugin. Most of my plugins are just a direct translation from AFL

i wonder if even before pushing the “reply” button on this site people can see what you are editing. When I read the reply I suddenly got to see an additional paragraph. Or is this because one edits a post and pushes “post edit”


#27

This is because of edits done AFTER original post.
Discourse is able to update the text you are reading after somebody edited his/her response without reloading entire page.


#28

I respect you opinions but in my case it not feasible to pass variable thru the function call. I be passing way over 100. Also i am using the old analysis because I am creating a new database and I don’t want threading messing up the records.