We're migrating to 11.6 and I think it's a great moment to rethink old habits and improve some concepts.
One of these things is the way we've been dealing with parameter definitions in functions and procedures.
Often times we have procedures and functions that need a lot of parameters, either inputs or outputs. Personally, for readability and maintainability reasons, I don't like to have methods with too many parameters explicitly declared.
In order to avoid this problem and still allow a large number of parameters, we've manually implemented a key-value pair approach with a single parameter.
But there are some drawbacks with this approach:
It's not possible to tell which parameters are needed just by inspecting
the method signature.
You'll always need some boilerplate code, like methods for pushing and pulling values.
So with that said, I would like to hear some others' thoughts.
Have you ever implemented something similar?
Is there something that could work as a javascript/json object in ABL?
Current implementation.
DEFINE VARIABLE param as CHARACTER NO-UNDO.
addValue('id', '1', param).
addValue('date', STRING(TODAY), param).
RUN internalProc (INPUT param).
Desired implementation
param.id = 1
param.date = TODAY
RUN internalProc (INPUT param)
Since you are mentioning 11.6, why not use a real class based object (available since 10.1A).
yourpackage\yourparameter.cls:
CLASS yourpackage.yourclass:
DEFINE PUBLIC PROPERTY date AS DATE NO-UNDO
GET.
SET.
DEFINE PUBLIC PROPERTY id AS INTEGER NO-UNDO
GET.
SET.
CONSTRUCTOR PUBLIC yourclass ():
SUPER ().
END CONSTRUCTOR.
CONSTRUCTOR PUBLIC yourclass (pid AS INTEGER, pdate AS DATE):
SUPER ().
ASSIGN THIS-OBJECT:id = pid
THIS-OBJECT:date = DATE .
END CONSTRUCTOR.
END CLASS.
and the internal procedure:
DEFINE INPUT PARAMETER poParameter AS yourpackage.yourclass NO-UNDO .
and the caller:
DEFINE VARIABLE o AS yourpackage.yourclass NO-UNDO.
o = NEW yourpackage.yourclass().
o:id = 42.
o:date = TODAY.
RUN internalProc (o) .
alternative caller:
RUN internalProc (NEW yourpackage.yourclass (1, TODAY)) .
The ABL provides full OO capabilities from 10.1A on and that can be mixed nicely with procedural code. And parameter objects (structs) is a great way to get started with a few inital classes in legacy code.
I have a macro A that formats some text
<#macro A text>...${text}...</#macro>
and another macro that has a parameter accepting text
<#macro B x>Another ${x} text</#macro>
I'd like to call B with the x paramter to be some text formatted by A, s.th. like
<#B x="<#A text='abc'/>" /> returns Another <#A text='abc'/>
Is this possible somehow?
I tried the ?interpret as suggested here by ddekany -
<#B x="<#A text='abc'/>"?interpret /> but this fails with the error:
Expecting a string, date or number here, Expression .... is
instead a freemarker.core.Interpret$TemplateProcessorModel
It seems that a macro call in FreeMarker is something different than a function call in other languages.
Macro calls aren't expressions, and hence can't be used inside expression (like a parameter value). Macros are called for their side effects, which is typically printing to the output, and have no return value. Functions (see #function) are called for their return values, and so function calls are expressions. So maybe you need functions, not a macros in this case.
But if you absolutely have to use the output of a macro call in an expression (or of any arbitrary template fragment), then you have to capture the output via <#assign someVar>...</#assign> or <#local someVar>...</#local>. (Beware with #escape. If you re-print the captured output with ${...}, it will be escaped again, so you will need #noescape.)
I found a workaround using assign:
<#assign a><#A text="abc"/></#assign>
<#B text=a/>
Anyway, it would be interesting to know if this is possible somehow.
We know that scala does not support more than 22 params, but if i write this
def echo(args: String*) = for (arg <- args) println(arg)
we can use more than 22 params to call this function like this.
echo("1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1")
But I think this is an array. So, it can do that and i tried this
val a = Array[String]("1","2","3");echo(a)
This code must be wrong, so here's my first question, why is this happening?
and, if i try to write this
echo(a : _*)
It's right,the second question is, what does this sign means '_*'? I can't use this code in other ways like in for(). So, is echo(a : _ *) is a right code?
The echo function is defined to take a variable number of string arguments. This is really only syntactic sugar; the compiler will insert the necessary instructions to wrap the arguments in an array and then pass the array. So the function will actually only receive a single argument at runtime.
The reason you can't pass the array directly is that there is no additional compiler logic to automagically figure out that the string arguments are already wrapped. The function declaration indicates that zero or more strings are expected, the parameter is actually an array, and a compiler error results.
The : _* notation is additional syntactic sugar to account for this problem; by using this syntax you indicate to the compiler that you are intentionally passing an array instead of the variable number of string parameters.
This may seem odd and is perhaps impossible but I was wondering if there's a way to create a macro that evaluates the expression passed and performs an import.
I can get it to work easily enough if a string literal is the expression:
import macros
macro createImport(ex: expr): stmt =
result = newNimNode(nnkImportStmt)
result.add(ex)
createImport("strutils")
let a = ["foo", "bar", "baz"]
echo a.join("---") # using `join()` from the `strutils` module
But if a variable is passed, this of course will fail.
var s = "strutils"
createImport(s)
(Note that the import could be a string path do a module.)
I've attempted many adjustments to the macro and scoured the docs and source but I just can't find a way to get the actual value of the ex: expr to be useful in the import.
I can get the macro to create an echo call that reveals the string passed but any attempt to use it with the import ends up using the variable name itself.
I guess it makes since since it would seem that the value may not be available when the macro itself is evaluated. Is it possible to do this and if so, how?
I'm not quite sure why you need such a helper macro and whether it's a good idea, but the simplest way to achieve what you need is the following:
template my_import(x: static[string]) = import x
const x = "strutils"
my_import x
How can I modify a parameter inside a CMake Macro?
I'm using CMake version 2.6 and I can't update the variable!
Here's a basic example:
# macro definition
MACRO(MYTEST RETVAL)
message("input RETVAL=${RETVAL}")
SET(RETVAL "new return value")
message("after update RETVAL=${RETVAL}")
ENDMACRO(MYTEST)
# call macro with parameter '_test' set to 'init'
SET(_test "init")
MYTEST("${_test}")
message("after macro call:${_test}")
This prints:
input RETVAL=init
after update RETVAL=init
after macro call:init
The variable _test is never modified. What can I do to make it work?
Thanks
With CMake macros, you need to very carefully distinguish between macro parameter name, variable name, variable value etc. It's not quite clear from your CMake code what you want to achieve, but I assume you want to set _test to the string new return value using the macro.
This is the code to accomplish that:
macro(MYTEST RETVAL)
message("input variable name: RETVAL=${RETVAL}")
message("input variable value: ${${RETVAL}}")
set(${RETVAL} "new return value")
message("variable name after update: RETVAL=${RETVAL}")
message("variable value after update: ${${RETVAL}}")
endmacro()
set(_test "init")
MYTEST(_test)
message("after macro call: ${_test}")
Points to note:
You want the macro to modify the variable which was passed in. That variable's name is stored in the macro parameter RETVAL. So you need to set(${RETVAL} ...) to set the varibale. Your code was creating a variable named RETVAL.
You need to pass the name of the variable to change to the macro. So you must pass _test, and not ${_test}. Your code was calling the macro with the text init.