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