I agree entirely that macros and eval should be avoided if possible - so
why have you used them for memoization?

const func_pointers = Dict{String, Ptr{Void}}()

getFuncPointerM(name) =
  haskey(func_pointers, name) ?
    func_pointers[name] :
    (func_pointers[name] = getFuncPointer(name))


Perhaps I'm missing something, but that seems equivalent and a lot neater
to me. The benefit to your macro is that it's faster, which is exactly the
reason to use eval in Simon's case.

If retrieving a pointer carries a significant overhead compared to inlining
it (which may well be a concern in an OpenGL library), then using eval to
do that inlining is perfectly justified.


On 3 April 2014 02:13, Jameson Nash <[email protected]> wrote:

> A delayed macro is just a function call. Don't make you life
> complicated by trying to use one in place of the other.
>
> Using macros and eval can get you into a lot of trouble by helping you
> write brittle code. They allow you to confuse compile and run time,
> even though Julia does make a strong distinction between them. Avoid
> using them.
>
> The correct way to write this is using memoization. We can use a macro
> to make this look neater. This one is copied from PyCall:
>
> macro getFuncPointer(func)
>     z = gensym(string(func))
>     @eval global $z = C_NULL
>     quote begin
>         global $z
>         if $z::Ptr{Void} == C_NULL
>             $z::Ptr{Void} = $(getFuncPointer(esc(func))))
>         end
>         $z::Ptr{Void}
>     end end
> end
>
> glGetString(name::GLenum) = ccall(@getFuncPointer("glGetString"),
> ...., ...., name)
>
>
> A good macro should be equivalent to a pure function: regardless of
> when or how it is run, or how the program state changes, it must
> return the same result given the same inputs. In this case, it returns
> a static variable and a method of retrieving the actual function
> pointer. Note that it does not actually look up the function pointer
> or return it -- that cannot be done until runtime.
>
> eval is actually just a macro call in disguise, so the same guidelines
> apply. This also happens to demonstrate an appropriate (safe) use of
> eval.
>
>
> PS. this code should be causing a compile-time deprecation warning
> (because of the constant there), I'll have to investigate why that
> code is not working anymore:
>         return top(ccall)(Ptr{Void}
> @0x00007f402e6dadc0,Ptr{Int8},(Uint16,),name::Uint16,0)::Ptr{Int8}
>
> On Wed, Apr 2, 2014 at 11:56 AM, Isaiah Norton <[email protected]>
> wrote:
> > I stand corrected.
> >
> > On Wed, Apr 2, 2014 at 9:50 AM, Simon Danisch <[email protected]>
> wrote:
> >>
> >> Ah, the links are all the same...
> >> Output for Mikes solution:
> >> native: https://gist.github.com/SimonDanisch/6270c01a6ea881877c4f
> >> llvm:   https://gist.github.com/SimonDanisch/612e8b08d915d188c4d5
> >>
> >>
> >>
> >>
> >> 2014-04-02 15:46 GMT+02:00 Simon Danisch <[email protected]>:
> >>
> >>> @ Mike
> >>> Good question. Well, I definitely would wish for a simpler solutions.
> >>> But as far as I understand the situation, every video card vendor makes
> >>> his own OpenGL implementation.
> >>> Also, even on one computer, you can have more than one rendering
> context.
> >>> This means, the correct function pointers can be just queried, after
> one
> >>> creates a particular context.
> >>>
> >>>
> >>> So I looked into the machine and llvm code and its pretty clear!
> >>> The version inspired by Mike generates a lot shorter llvm and machine
> >>> code.
> >>> It also doesn't matter if I call glGetString before inspecting the
> >>> machine code.
> >>>
> >>> My test program for Isaiah's solution:
> >>>
> >>> #glTest.jl
> >>>
> >>>
> >>>
> >>>
> >>>
> >>> using GLUT, OpenGL
> >>>
> >>> glutInit()
> >>>
> >>>
> >>>
> >>>
> >>>
> >>> glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA |
> >>> GLUT_MULTISAMPLE | GLUT_ALPHA)
> >>>
> >>>
> >>>
> >>>
> >>>
> >>> glutInitWindowPosition(0, 0)
> >>> glutInitWindowSize(1,1)
> >>>
> >>>
> >>>
> >>>
> >>>
> >>>
> >>> window = glutCreateWindow("dummy")
> >>>
> >>>
> >>>
> >>>
> >>>
> >>>
> >>> println(code_llvm(glGetString, (Uint16,)))
> >>>
> >>>
> >>>
> >>>
> >>>
> >>>
> >>> println(bytestring(convert(Ptr{Uint8}, glGetString(0x1F02))))
> >>>
> >>>
> >>>
> >>>
> >>>
> >>>
> >>> println(code_llvm(glGetString, (Uint16,)))
> >>>
> >>>
> >>>
> >>>
> >>>
> >>>
> >>> #OpenGL.jl
> >>> module OpenGL
> >>>
> >>> function getFuncPointer(name)
> >>>
> >>>
> >>>
> >>>
> >>>
> >>>     ccall( (:glXGetProcAddress, "libGL"), Ptr{Void}, (Ptr{Cchar},),
> name)
> >>>
> >>>
> >>>
> >>>
> >>>
> >>> end
> >>> glGetString(name::Uint16) = ccall(getFuncPointer("glGetString"),
> >>> Ptr{Cchar}, (Uint16,), name)
> >>>
> >>>
> >>>
> >>>
> >>>
> >>>
> >>> export glGetString
> >>> end
> >>>
> >>>
> >>> Output:
> >>> https://gist.github.com/SimonDanisch/777bfc7783f9e96818f4
> >>>
> >>> Output for Mikes solution:
> >>> native: https://gist.github.com/SimonDanisch/777bfc7783f9e96818f4
> >>> llvm:    https://gist.github.com/SimonDanisch/777bfc7783f9e96818f4
> >>>
> >>>
> >>> Am I missing something?
> >>>
> >>>
> >>> Am Dienstag, 1. April 2014 14:30:12 UTC+2 schrieb Simon Danisch:
> >>>>
> >>>> Hi,
> >>>> I'm working on the OpenGL package and I want to make it finally usable
> >>>> in a nice and clean way on all platforms.
> >>>> The problem is, that one needs pointer for the GL functions, which you
> >>>> can only get, after initialization of the OpenGL context.
> >>>> But initializing the context and creating a window shouldn't be part
> of
> >>>> the OpenGL package.
> >>>>
> >>>> So I tried two different approaches, which both seem to have their
> >>>> downsides:
> >>>>
> >>>> 1.
> >>>> Initialize OpenGL context when including the OpenGL package
> >>>> This is bad, because this makes the OpenGL package dependent on some
> >>>> third party OpenGL context creation library.
> >>>>
> >>>> 2.
> >>>> Load the functions later with a loading Function.
> >>>> Bad, because the function definitions are not visible for any other
> >>>> module, that relies on the OpenGL package.
> >>>>
> >>>> My ideal solution would be, to evaluate a macro when the function is
> >>>> called and not when the module is included.
> >>>> Like this, I can define all the OpenGL functions already in the OpenGL
> >>>> module, and when you call them the first time,
> >>>> the right function ptr gets inserted into the ccall, or an error is
> >>>> raised, when OpenGL context is not initialized.
> >>>>
> >>>> this could look like this:
> >>>>
> >>>> module OpenGL
> >>>>
> >>>> macro getFuncPointer(name::ASCIIString)
> >>>>    return getProcAddress(name)
> >>>> end
> >>>>
> >>>> glGetString(name::GLenum) = ccall(@getFuncPointer("glGetString"),
> ....,
> >>>> ...., name)
> >>>> export glGetString
> >>>> end
> >>>>
> >>>> using OpenGL
> >>>> ...create OpenGL context
> >>>> #define getProcAddress
> >>>> global const getProcAddress = glutGetProcAddress # If using GLUT for
> GL
> >>>> context creation
> >>>> #call gl Functions
> >>>> glGetString(GL_VERSION)
> >>>>
> >>>> Any ideas how to do this in a clean way?
> >>>>
> >>>>
> >>>> Cheers,
> >>>>
> >>>> Simon
> >>
> >>
> >
>

Reply via email to