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