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

Reply via email to