How do i determine if a variable exist?

No you can’t get list of all variables from DLL.
As I said, you should not communicate via variables and you should not rely on existence of variables or lack thereof.

The plugin should rely on passing arguments to function and returning value from the function. That is proper way to code the plugin.

You need to understand that your plugin will be called from MULTIPLE THREADS, and you won’t know which thread variable you are reading now.

It is very bad idea to communicate via variables. Use arguments and return value. That is the ONLY way.

1 Like

Do we not rely on the existent of open low high close and volume. All i am doing is making sure that a array is in the afl. I cant see how this is bad programming. I need to make sure the array myentry exist by the plugin. If the variable doesn’t exist exit the dll. Oh I get your point have the plugin pass the array. I can work that way to. But i am passing over 14 variables now

Hows that going to work sometimes I need to pass 6 more variable other time it may be only 3
I still think checking for existent is a better.

Hi @nickhere,

Several of my DLL files used to rely on passing data directly to afl variables and it was a huge mess.

On Tomasz recommendation I rewrote my functions to pass everything as arguments. This is the best way to do things. He is absolutely correct.

You can create as many functions as you need to pass different numbers of variables or write one that accepts a superset and pass a variable to tell your function which to use.

-Alan

DLLs support variable argument count.
The thing is that you provide default values for OPTIONAL parameters. And these optional parameters can but do not need to be provided by calling code. If given argument is not passed, it gets the default value.
See the AmiBroker Development Kit documentation for the details.

I forgot about optional parameters. Thanks for clarifying.

can you give me a example

so i should do this
myfunction(myentry1,myentry2,myentry3);
but if i do this
myfunction(myentry1);

what would tell me that only 1 has been passed

I have never coded default values into my DLL, so to avoid giving inaccurate advice I cannot say much about it. It is covered in section 2.4 of the ADK documentation. It appears that default values can only be applied to float arguments, not arrays or strings.

If your arguments are floats, select a default value that would indicate to your function that nothing was passed.

My original comment about having multiple functions would look like this (different functions depending on the number of arguments):

myfunction1(myentry1);
myfunction3(myentry1, myentry2, myentry3);

Or for a superset:

myfunction(myentry1, myentry2, myentry3);

You would send dummy arguments to the ones you are not using, letting your function know to disregard them.

Or…

myfunction(numArgs, myentry1, myentry2, myentry3);

Where numArgs tells your function how many arguments to use. You would still need to send dummy arguments in the unused entries.

@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.

3 Likes

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);
	}

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.

3 Likes

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.

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.

1 Like

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”

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.

1 Like

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.

In the C++ plugins we use this constant for NULL, right?
EMPTY_VAL

We need to distinguish two cases:

  1. The variable is not defined in afl code ==> the above code will work fine
  2. The variable is defined in afl code but is NULL (it mean isNULL(test) returns true in afl) ==> the above code will not work. So in C++ we need to check both if the variable not exists and if it exists but is NULL.

i will check as following:
AmiVar var = gSite.GetVariable("test");
if( var.type == VAR_NONE || IS_EMPTY(var.val)) { // does not exist or null ==> assign var to other thing }