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