Hi Simon,

I have not to much experience with macros. But the solution Jamson outlined 
where the function pointer is cached in a global seems to me pretty 
straight forward to me. And if this is packed into a macro which is already 
used in the (very solid) PyCall package this seems to be the way to go.

Cheers,

Tobi

Am Donnerstag, 3. April 2014 10:21:52 UTC+2 schrieb Simon Danisch:
>
> 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" <vtj...@gmail.com <javascript:>> 
> 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 
>> <mike.j...@gmail.com<javascript:>> 
>> 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 <vtj...@gmail.com <javascript:>> 
>> 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 
>> >> <isaiah...@gmail.com<javascript:>
>> >
>> >> wrote:
>> >> > I stand corrected.
>> >> >
>> >> > On Wed, Apr 2, 2014 at 9:50 AM, Simon Danisch 
>> >> > <sdan...@gmail.com<javascript:>
>> >
>> >> > 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 
>> >> >> <sdan...@gmail.com<javascript:>
>> >:
>> >> >>
>> >> >>> @ 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