ENIGMA Forums
General fluff => General ENIGMA => Topic started by: RetroX on December 05, 2010, 03:29:29 pm
-
This is one thing that I stumbled upon today. Like execute_string, these functions will be a bit tricky. And I happened to use one in a GM project, and they don't work in ENIGMA currently.
variable_global_exists(name)
variable_global_get(name)
variable_global_array_get(name,ind)
variable_global_array2_get(name,ind1,ind2)
variable_global_set(name,value)
variable_global_array_set(name,ind,value)
variable_global_array2_set(name,ind1,ind2,value)
variable_local_exists(name)
variable_local_get(name)
variable_local_array_get(name,ind)
variable_local_array2_get(name,ind1,ind2)
variable_local_set(name,value)
variable_local_array_set(name,ind,value)
variable_local_array2_set(name,ind1,ind2,value)
I'm not entirely sure how to go about doing these, but the best idea that I can think of is keeping an array of variable names from the parser and storing pointers during runtime. It would really decrease speed, though, and I think that it would be best to make this kind of system enable/disable-able. Nonetheless, these do happen to be relatively common functions that will have to be supported by ENIGMA.
Usually, code tends to be:
if (variable_global_exists('bob'))
{
bob = global.bob;
}
which would cause a compile error in C++.
You also face the problem of:
bobvar = "bob"
if (variable_global_exists(bobvar))
{
bob = global.bob;
}
Which would run into even more problems.
-
Yeah, I have used them too. There are always ways to not use them and make that particular piece of code another way. But it is still nice to use them, for example, in translations. You could have a file like so:
introduction="This is blah blah"
And in the game you could have something like:
variable_global_set("str_"+variable,string);
And then just use str_introduction anywhere where you want to use your string. Its better than to save them inside arrays or something because you can more easily get the correct string. Arrays and such of course would be faster.
edit: Also, my example works only if variable_global_set actually creates the variable as well. I haven't tested so I don't know. In GM I actually used execute_string() to do this. I used variable_ functions for other things, like setting correct values to checkboxes with names etc.
-
There's no way to implement them... efficiently. Doing so requires two modifications. They're difficult to make, but only because it pains me to do so. Each variant needs a bool to indicate whether it's been assigned, then each operator needs overloaded to a hash table needs constructed in each instance for new variables added, and a table needs constructed for each object to reference hard-compiled variables.
Honestly, I hardly intend on doing it. But I'm sure someone will cry about it at some point and I'll implement it.
-
since enigma allows pointers the *_set_* ones are easy enough to work around. honestly you should really need these
-
...
-
it would be a massive map of pointers, which would be slow as hell
-
These are better replaced by string indices
-
Regarding global variables, I'm pretty sure some nasty executable file format-based hacks might work. However, only if the variable type is known ahead-of-time. In other words, if you declared a global int x and then accessed it as a var it would fail miserably.
I mean, LLVM does stuff like that.
Note that on Windows it could require declaring all globals with __declspec(dllexport).
All this without requiring massive maps of pointers.
For local variables, though, you guys would be totally screwed.
-
Regarding global variables, I'm pretty sure some nasty executable file format-based hacks might work. However, only if the variable type is known ahead-of-time. In other words, if you declared a global int x and then accessed it as a var it would fail miserably.
I mean, LLVM does stuff like that.
Note that on Windows it could require declaring all globals with __declspec(dllexport).
All this without requiring massive maps of pointers.
For local variables, though, you guys would be totally screwed.
That would only work if the game was compiled as a shared library, and it wouldn't work with how ENIGMA's instances are organised.
You can't just access variables by symbol at runtime; it doesn't work that way.
-
@RetroX I do not see how this would conflict with ENIGMA's instances.
Also, see http://llvm.org/docs/tutorial/LangImpl4.html#jit
Executables are basically shared libraries.
See also GetModuleHandle(NULL) and GetProcAddress (Windows-only, Linux has similar functions)
EDIT: Windows source-code:
#include <stdio.h>
#include <windows.h>
typedef void (*func)(const char*);
extern "C" {
__declspec(dllexport) char* message = "HELLO WORLD!";
__declspec(dllexport) void display_message(const char* msg) {
printf("Message: %s\n", msg);
}
}
int main() {
HMODULE module = GetModuleHandle(NULL);
char** msg = (char**) GetProcAddress(module, "message");
func disp = (func) GetProcAddress(module, "display_message");
disp(*msg);
return 0;
}
This works fine as an executable, without compile-time tricks. Note that this is NOT a DLL.
This probably would also work on Linux with some minor changes(dlopen instead of GetModuleHandle?)
-
@RetroX I do not see how this would conflict with ENIGMA's instances.
Also, see http://llvm.org/docs/tutorial/LangImpl4.html#jit
Executables are basically shared libraries.
See also GetModuleHandle(NULL) and GetProcAddress (Windows-only, Linux has similar functions)
EDIT: Windows source-code:
#include <stdio.h>
#include <windows.h>
typedef void (*func)(const char*);
extern "C" {
__declspec(dllexport) char* message = "HELLO WORLD!";
__declspec(dllexport) void display_message(const char* msg) {
printf("Message: %s\n", msg);
}
}
int main() {
HMODULE module = GetModuleHandle(NULL);
char** msg = (char**) GetProcAddress(module, "message");
func disp = (func) GetProcAddress(module, "display_message");
disp(*msg);
return 0;
}
This works fine as an executable, without compile-time tricks. Note that this is NOT a DLL.
This probably would also work on Linux with some minor changes(dlopen instead of GetModuleHandle?)
Yes, this works with globals. It does not work with locals.
Also, a string map would probably be faster.
-
GetProcAddress uses a string map constructed with the symbols declared __declspec(dllexport). It's the same thing but a string map gives you control over the implementation and is cross-platform.
-
I'm just saying that the OS already provides the functionality built-in.
variable_get_global/variable_set_global would probably be 10 lines of code long(and that's counting the platform differences!) and the parser would probably need one line changed for Windows compatibility.
Efficient or not, it's simple to implement, and it does not feel like a hack.
Also, regarding whether the string map would be faster. This kind of thing is built-in by the OS, so I'm guessing it's pretty optimized by now. That is, unless it uses kernel system calls via interrupts or something like that. But even if the string map is faster, would it even be worth the trouble considering how simple this solution is?
-
GetProcAddress uses a string map constructed with the symbols declared __declspec(dllexport). It's the same thing but a string map gives you control over the implementation and is cross-platform.
I also wasn't thinking when I made that other post. :V
Yeah, you're right. That makes more sense, now that I think about it.
The problem that I still see is that you're getting a symbol for a variable. You're not getting its value; that value is stored in memory, and not the binary.
-
I'm getting the POINTER to the variable.
If you are wondering, this is the behavior:
int signal = 0;
...
printf("%d\n", signal); //Prints 0
*GetProcAddress(GetModuleHandle(NULL), "signal")) = 2;
printf("%d\n", signal); //Prints 2
-
Ah, okay. I thought that you were just loading the symbols from the binary; apparently, you're just loading the symbols from the currently loaded part in memory.
-
That's not quite how it works either. The binary has a symbol table that stores the addresses of names. It may or may not be in memory depending on how the kernel and dynamic linker work. GetProcAddress returns that address, meaning you have a pointer to the variable in memory - symbols are better thought of as "pointers" even if they don't use pointer syntax within the language.
-
That makes sense, then. I just assumed that the binary's pointers would be inaccurate because it would be different if that one pointer was taken.
-
In some cases, especially dynamically linked binaries, it would be. Relocation tables for position-independent executables store all the locations in the binary that need to be updated when symbol addresses change because of where they're loaded in memory. However, GetProcAddress takes care of any of that so any difference is just another layer invisible to the programmer.