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
>> >>
>> >>
>> >
>
>