Calling functions in other packages.

In version 1.7.1.0, we have included the feature of calling functions in other ACF packages from the ACF package you are currently working with. As it always has been possible to use eval (ACF_run("other acf function in a different package";params)), it is not very effective and introduces a significant overhead. The new efficient way to do it is our new inter-package calling scheme that makes us able to have libraries of ACF functions that can be used from new packages. This simplifies the implementation of new features using standard ACF libraries, for example, an EXCEL library that makes it much simpler to produce EXCEL reports.

The USE statement

Right below the package declaration (and before any function declaration), write:

USE package_name; 

The other package needs to be loaded when you compile your source code. In this way, the compiler can syntax-check your function call against the function in the other package.

After that, you can simply call the functions in that package as if they were local functions. Just write the name and the parameter list. In this way, the calling scheme induces very little overhead.

Let's say, in the bootstrap library that comes pre-installed, there is a function to return the filename from a full path:

package bootstrap "bootstrap library";
...
...
...
function GetFilenameFromPath ( string path ) 
    FunctionID 205; 
    path = substitute ( path, ":", "/");
    path = substitute ( path, "\\", "/");
    return regex_replace("^(.+)/(.+\..+)$", path, "\2");
end
...
...
...

Now we are working on a different package, so we need this function.

package testnewstuff "test some new stuff"; 

USE bootstrap; // tell us that all functions in that package are available here...

Function test_ext_functions ( string path )
    string cc = GetFilenameFromPath ( path ); 
    return cc; 
end

In the FileMaker Calculation:

Set Variable [$fn; ACF_Run("test_ext_functions";"/Users/peter/Desktop/MyFile.txt")]

Now, the $fn will contain MyFile.txt

Performance metrics

To see how the inter-package calls perform compared to local functions, I did a small test. I wrote some functions to trim away white space left, right, and both sides of a given string. Like this:

Function leftTrim (string s)
    FunctionID 240; 
    return regex_replace ("^\s+", s, "");
end

function rightTrim (string s)
    FunctionID 241; 
    return regex_replace ("\s+$", s, "");
end

function Trim (string s)
    FunctionID 242; 
    return leftTrim(rightTrim(s)); 
end

Then I made a test function to see the performance:

function testTrim (string s, int z)
    int i;
    string x; 
    for (i=1,z)
        x = Trim(s);
    end for
    return x; 
end

When called from FileMaker calculation, with a given string with some spaces on each side, and the z-value of 10,000; the time including the call from FileMaker was about 155 mS.

Then I moved the trim functions to a different package and used the "USE" statement. With the same string and z-value, the time increased to 165 mS.

The difference was also 10 mS for a loop with 10,000 rounds, which tells that the overhead with inter-package calls is only 1 µS.

An overhead of 1 µS per call for inter-package communication is typically considered negligible in most application contexts, especially considering the benefits of modularity, code reuse, and separation of concerns that come with organizing functionality across packages.

Compared to the standard custom function

Brian Dunning's CF site has a Trim4 standard custom function. I did the same test with this, repeating the call to it 10,000 times: The execution time increased to 1718 ms (or 1.718 seconds). This is 0.1718 mS for each call. Compared to the ACF trim function that runs 10,000 Trim calls in 155 mS/10,000 = 0.0155 mS or 15.5 µS for each call. That makes our function 11 times faster. Our Trim function uses two regex operations that are probably not the fastest. Later we will have a trim function as an optimized plugin function.

I tried both as a calculation with a while statement, a script with a loop, and an ACF function using the FileMaker calculation engine with the @....@ notation. They all executed in the range 1708 to 1730 ms.

function testTrim4 (string s, int z)
    int i;
    string x; 
    $$val = s; 
    for (i=1,z)
        x = @Trim4($$val)@;
    end for
    return x; 
end

Can we optimize it some more?

Well, I actually did implement the trim functions into the ACF language as standard native functions to see the effect of using this instead. I created three native functions for this that are now standard functions in the ACF language:

Then I changed the function in this way:

function testTrim (string s, int z)
    int i;
    string x; 
    for (i=1,z)
        x = TrimBoth(s);
    end for
    return x; 
end

And the result was really impressive, with 11.64 mS for all 10,000 operations altogether. This means that each trim operation on both sides used only 1.164 µS. That is 148 times faster than the standard FileMaker Custom Function implementation.

Some points to notice

A new version of the development tool

We have created a new version of the development tool that better supports this new function. The new tool will soon be released.