Julia - How macros are related (based on exemple from OpenCL.jl

Viewed 42

I've put a discord but no result. May be here somebody could help me

When you look in the OpenCL implementation code you have two macros that seems to refer one another in some (misterious) way, I cannot see how this happens. One macro is @check define in src/ macros.jl the other one @ocl_func define in api.jl. @check is responsible for running the clfunc => $(esc(clfunc)), but as far as I understood it is the @ocl_func macro (in the api.jl file) that will ccall the clfunc.

My question how does the 'called' clfunc, for example this one:

@check api.clGetPlatformIDs(0, C_NULL, nplatforms) (in /platforms.jl) 

is passed to the @ocl_func macro?

Is it implicit( because the ocl macro is define in api so every function called with api.will activate the macro, in the example it will be then:

@ocl_func(clGetPlatformIDs(0, C_NULL, nplatforms)) )

I cannot find the code which glue the two macros. Clarifying this will help me to understand how macros work.

2 Answers

An easy way to think about macros is that they are source-code generation tools.

as far as I understood it is the @ocl_func macro (in the api.jl file) that will ccall the clfunc.

If you look at the the implementation of @ocl_func, you'll see that the ccall is part of a quote block, that's being returned. So it's not that @ocl_func will ccall the clfunc, rather @ocl_func generates the source code expression which ccalls the clfunc. The quoted block that is returned by a macro is seen as source code by Julia and is compiled like any other code, a function definiton in this case.

So when Julia sees the line:

@ocl_func(clGetPlatformIDs, CL_int,
              (CL_uint, Ptr{CL_platform_id}, Ptr{CL_uint}))

this gets turned into a function definition that uses ccall inside it.

Similarly, @check also generates code that does an error check and throws if it's not a success. @check doesn't need to know about @ocl_func, instead both just expand into the quoted code that they return, so any interaction is between those generated code blocks.

My question how does the 'called' clfunc, for example this one:

@check api.clGetPlatformIDs(0, C_NULL, nplatforms) (in /platforms.jl)

is passed to the @ocl_func macro?

The function definition for api.clGetPlatformIDs is generated by the @ocl_func macro, but once its generated it can be called like any normal function, and that's what's happening here. So there's no dependency between @check and @ocl_func necessary for this to work.

While Sundar's answer explains what is going on it is always a good idea to try @macroexpand too:

julia> @macroexpand OpenCL.cl.@check api.clGetPlatformIDs(0, C_NULL, nplatforms)
quote
    #= C:\JuliaPkg\Julia-1.8.1\packages\OpenCL\yqbHv\src\macros.jl:3 =#
    local var"#3#err"::OpenCL.cl.CL_int
    #= C:\JuliaPkg\Julia-1.8.1\packages\OpenCL\yqbHv\src\macros.jl:4 =#
    var"#3#err" = api.clGetPlatformIDs(0, C_NULL, nplatforms)
    #= C:\JuliaPkg\Julia-1.8.1\packages\OpenCL\yqbHv\src\macros.jl:5 =#
    if var"#3#err" != OpenCL.cl.CL_SUCCESS
        #= C:\JuliaPkg\Julia-1.8.1\packages\OpenCL\yqbHv\src\macros.jl:6 =#
        OpenCL.cl.throw(OpenCL.cl.CLError(var"#3#err"))
    end
    #= C:\JuliaPkg\Julia-1.8.1\packages\OpenCL\yqbHv\src\macros.jl:8 =#
    var"#3#err"
end
Related