Can someone explain me what exactly is brittle about it?
I don't like the solution too much myself, as it disguises things a little.

But I don't know Julia ( or macros and programming concepts in general)
well enough to see the bad side effects.
At least it gets the job done, which is to generate a function body that
only consists of a ccall with an inlined function ptr.

Or is this not desirable?
On Apr 3, 2014 5:19 AM, "Jameson Nash" <[email protected]> wrote:

> As I indicated, I used the macro to make the code look neater. I could
> have expanded it (see below). But just as functions help DRY code,
> macros help DRY typing. Memoizing using a dict isn't much better than
> calling dlsym, which is also some form of dict (and may even benefit
> from better memory localization). I am doing almost the same (using
> the module global namespace as a dictionary), but I am cheat because I
> can inform the compiler that the result of this dictionary lookup is
> static, which lets it emit a direct entry into the module global
> lookup table, at essentially negligible runtime performance impact
> (one mov and one jmp instruction more than directly ccall'ing a
> pointer).
>
> Using eval to do inlining is a brittle usage of both eval and
> inlining. it is nice to have the computer run things fast, but it is
> better have it do them right :)
>
> const glGetString_func_pointer = C_NULL
> function glGetString(name::Uint16)
>     global glGetString_func_pointer
>     if glGetString_func_pointer::Ptr{Void} == C_NULL
>         glGetString_func_pointer::Ptr{Void} = getFuncPointer("glGetString")
>     end
>     ccall(glGetString_func_pointer::Ptr{Void}, Ptr{Cchar}, (Uint16,), name)
> end
>
> On Wed, Apr 2, 2014 at 9:59 PM, Mike Innes <[email protected]> wrote:
> > 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