I'm trying to use LeMP to generate some C# bindings for a C++ library, and as part of this I need to generate a string that combines together some arguments from a LeMP macro to use in the DllImport EntryPoint value. Looking at the docs, it seems like a combination of concatId and stringify should do the job, but I can't get it to work. Here's a slightly simplified version of the code in question:
define TypedIndexer2D($CONTAINER_TYPE, $T1, $T2)
{
replace(MethodName => concatId(Buffer, $CONTAINER_TYPE, GetExpr_, $T1, $T2));
replace(CFunction => concatId(buffer_, $CONTAINER_TYPE, _getexpr__, $T1, $T2));
[DllImport(Constants.LibName, EntryPoint = CFunction)]
public static extern IntPtr MethodName(IntPtr obj, IntPtr x, IntPtr y);
}
TypedIndexer2D(Int, Var, Var);
This emits the following:
[DllImport(Constants.LibName, EntryPoint = buffer_Int_getexpr__VarVar)]
public static extern IntPtr BufferIntGetExpr_VarVar(IntPtr obj, IntPtr x, IntPtr y);
However, I need this:
[DllImport(Constants.LibName, EntryPoint = "buffer_Int_getexpr__VarVar")]
public static extern IntPtr BufferIntGetExpr_VarVar(IntPtr obj, IntPtr x, IntPtr y);
(note the quoted EntryPoint).
I had thought that it would be something like the following:
replace(CFunction => stringify(concatId(buffer_, $CONTAINER_TYPE, _getexpr__, $T1, $T2)));
However that just emits the following:
[DllImport(Constants.LibName, EntryPoint = "concatId(buffer_, Int, _getexpr__, Var, Var)")]
How can I persuade LeMP to generate the string that I need here? Thanks!
The answer is indeed to run stringify
on the output of concatId
, but there's a trick to it.
The difficulty is caused by execution order. Macros ordinarily run "outside-in", outermost macro first, in contrast to normal functions that run "inside-out". Therefore
stringify(concatId(Tea, ring, Cot, ton));
produces "concatId(Tea, ring, Cot, ton)"
. There isn't a super elegant way to reverse the order yet - in custom define
macros you can use a [ProcessChildrenBefore]
attribute, but this doesn't let you to modify the existing behavior of stringify
. Here's a technique that works:
replacePP(xx => concatId(Tea, ring, Cot, ton)) { stringify(xx); }
// Output: "TearingCotton";
in contrast to normal replace
, replacePP
preprocesses the match and replacement expressions, thus concatId
happens before stringify
. Applying this solution to your TypedIndexer2D
, we get
define TypedIndexer2D($CONTAINER_TYPE, $T1, $T2)
{
replace(MethodName => concatId(Buffer, $CONTAINER_TYPE, GetExpr_, $T1, $T2));
replacePP(CFunction => concatId(buffer_, $CONTAINER_TYPE, _getexpr__, $T1, $T2));
[DllImport(Constants.LibName, EntryPoint = stringify(CFunction))]
public static extern IntPtr MethodName(IntPtr obj, IntPtr x, IntPtr y);
}