Hi, I have been trying out nim over the last couple of weeks and I am really
impressed with what can be done with the meta-programming specifically. My main
interest has been wrapping some existing C++ code bases and I have come up with
some macros that help me along the way (patching vTables etc.)
I have been using the {.emit.} pragma quite extensively in macros and since I
want to re-use those macros within a couple other .nim files/modules I put
these utility macros into their separate .nim file: (Please note that this is
not the actual code, the real code is not even concerned with sizeof() at all,
so please ignore that the purpose of the following code is not very useful. I
just tried to produce an as small as possible repro-case of what my code
actually does)
**cpp_tools.nim**
import macros
# The C++ code of this macro should best be emitted once into a .h file
# and then just #included into every NIM generated .cpp file that imports
the "cpp_tools.nim" module
macro sizeof_init*(): typed =
result = nnkPragma.newTree(
nnkExprColonExpr.newTree(
newIdentNode("emit"),
newLit("""
#include <cstdint>
template<typename T>
class CppSizer
{
public:
static uint32_t getSize()
{
return sizeof(T);
}
};
"""
)
)
)
# This macro generates a specialization of the above C++ template for a
given NIM type
# and then also injects a NIM wrapper proc to call the C++ "getSize()"
method from NIM
macro register*(base_type: typed): typed =
result = nnkStmtList.newTree()
let bt: string = $base_type.symbol
result.add(nnkPragma.newTree(
nnkExprColonExpr.newTree(
newIdentNode("emit"),
newLit("""class """ & bt & """_sizer : public CppSizer<""" & bt
& """> {};""")
)
))
# generate NIM proc wrapping the C++ static method
var super_proc = nnkProcDef.newTree(
nnkPostfix.newTree( # 0 -> method name
newIdentNode("*"),
newIdentNode("sizeof_" & bt)
),
newEmptyNode(), # 1 -> ???
newEmptyNode(), # 2 -> ???
nnkFormalParams.newTree( # 3 -> parameters
newIdentNode("cuint")
),
nnkPragma.newTree( # 4 -> pragmas
nnkExprColonExpr.newTree(
newIdentNode("importcpp"),
newLit(bt & "_sizer::getSize(@)")
)
),
newEmptyNode(), # 5 -> ???
newEmptyNode() # 6 -> ???
)
result.add(super_proc)
Run
**main.nim**
import cpp_tools
# emits C++ "CppSizer" class into the main.cpp file
# Would be better if it would happen automatically just by importing
"cpp_tools"
# and the generated C++ code was only generated once, put in a .h header
file and
# just included in all .nim files (i.e. resulting .cpp files) that use the
"cpp_tools" module.
sizeof_init()
# Code like the following is declared in multiple different .nim files
# across the project for many different NIM types and uses the generated
sizeof_*** procs
type Person* {.pure, final, exportc.} = object
firstname: string
lastname: string
register(Person)
echo "C++ ", sizeof_Person()
echo "NIM ", Person.sizeof
Run
I annotated my intents for what the usage of the nim macros and the generated
C++ code should look like in the best case in the above code. Is there some way
with existing nim macros, pragmas or some other language features that I could
achieve what I want ??
Thanks for any hints