문제

If I have

<cfset arr_arguments = ["a","b","c"]>
<cfunction name="someFunction">
 <cfargument name="someArgumentOne">
 <cfargument name="someArgumentTwo">
 <cfargument name="someArgumentThree">

</cffunction>

Is there any way to invoke someFunction with the arguments arr_arguments, similar to someFunction("a","b","c")? I of course know I can use argumentCollection to pass a (keyed) structure to a function, but I am specifically asking about passing in an (keyless) array. In JS this could be easily done with someFunction.apply(this,arr_arguments), but in coldfusion I just can't find any way of doing this.

도움이 되었습니까?

해결책

Unnamed arguments are passed into a function as a structure with numeric keys matching the positions of the arguments defined in the function arguments. So instead of passing the named arguments, you can convert your array to a struct with numeric keys, and then pass the struct in with argumentCollection:

<cfset arr_arguments = {"1"="a","2"="b","3"="c"}>
<cfset someFunction(argumentCollection=arr_arguments)>

You can easily convert an array to a struct with numeric keys like this:

<cfset args = {}>
<cfloop from="1" to="#arrayLen(arr_arguments)#" index="i">
    <cfset args[i] = arr_arguments[i]>
</cfloop>

다른 팁

In Coldfusion 10, you can use the invoke function to do this. It appears to be an undocumented way to pass an array of arguments in order. Example in cfscript:

invoke('', 'someFunction', ['a','b','c']);

The first argument of invoke is the component name (or an empty string for UDFs). The second is the function name, and third is an argument array. Note that both component and function names must be passed as strings.

I tested this both with and without defined argument names and the order of arguments was preserved.

Ok if you are looking to call a function with an arbitary array of arguments the best option I can suggest is to make a wrapper function. See below example code

<cfscript>
    function testMe(
        required string arg1,
        required string arg2
    ) {
        writedump(arguments);
    }

    function invokeFunction(
        required inFunction,
        required array functionArguments
    ) {
        var stcArguments = {};
        // Introspect to get the method parameters
        var arrFunctionArguments = GetMetaData(arguments.inFunction).parameters;
        // Now figure out what we are iterating to
        var numArgumentsToParse = Min(ArrayLen(arguments.functionArguments),ArrayLen(arrFunctionArguments));
        // Populate the arguments structure
        for (var i=1;i<=numArgumentsToParse;i++) {
            stcArguments[arrFunctionArguments[i].name] = arguments.functionArguments[i];
        }
        // And try to call it
        return arguments.inFunction(
            argumentCollection = stcArguments
        );
    }

    invokeFunction(testMe,[1,2]);           // Works fine
    invokeFunction(testMe,[1,2,3,4]);       // Just skips 3 and 4
//  invokeFunction(testMe,[1]);             // Errors due to not enough arguments
//  invokeFunction(fakeFunctionName,[1]);   // Errors due to undefined function
</cfscript>

This will of course error if either of the following happen

  • One or more arguments are not of correct type
  • The function doesn't actually exist
  • Not all required arguments are passed in

But you can handle that outside. This will use introspection to figure out the argument names, populate the structure accordingly, and call the requested function.

Here's a UDF which will do that for you:

<cfscript>
function test_function(a,b,c){      
    writeOutput("arguments.a"&arguments.a&"<br>");
    writeOutput("arguments.b"&arguments.b&"<br>");
    writeOutput("arguments.c"&arguments.c&"<br>");

    return arguments.a & "::" & arguments.b & "::" & arguments.c;
}

function invoke_positional(func,arr){
    var metaData = GetMetaData(func);
    var args={};
    for(var pos=1;pos<=ArrayLen(arr);pos++){
        args[metaData.parameters[pos].name]=arr[pos];
    }
    return arguments.func(argumentCollection=args);
}

data = ["StringOne","StringTwo",22];

result = invoke_positional(test_function,data);

</cfscript>

<cfoutput>
<p>
Returned: #result#
</p>
</cfoutput>

invoke_positional works by using the UDF's metadata to build up a named set of parameters and then uses them with argumentCollection=

So, one option I have found out to work is to create an arguments object separately from the actual real arguments scope and simply add the values to it using a numeric index.

<cfset args_input = createObject("java","com.naryx.tagfusion.cfm.engine.cfArgStructData").init()>
<cfloop from="1" to="#arraylen(arr_arguments)#" index="i">
    <cfset args_input[i] = arr_arguments[i]>
</cfloop>
<cfset someFunction(argumentCollection=args_input)>

If you encounter this situation using adobe coldfusion I would advice you to try

<cfset correctClass = arguments.getClass().getName()>

To get the string to pass to the createObject. Or if that doesn't work just have a dedicated function to return an empty arguments object

<cfset args_input = this.generateArgumentObject()>

and

<cffunction name="generateArgumentObject">
    <cfreturn arguments>
</cffunction>

Oh well, all I know for sure is that it will work for sure in OpenBD and based on a post I saw in the past on Ben Nadel's blog I am pretty sure this should work in Adobe ColdFusion as well.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top