ENIGMA Forums

General fluff => General ENIGMA => Topic started by: RetroX on December 05, 2010, 03:29:29 pm

Title: variable_* Functions
Post 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.

Code: [Select]
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:

Code: [Select]
if (variable_global_exists('bob'))
{
  bob = global.bob;
}
which would cause a compile error in C++.

You also face the problem of:
Code: [Select]
bobvar = "bob"
if (variable_global_exists(bobvar))
{
  bob = global.bob;
}

Which would run into even more problems.
Title: Re: variable_* Functions
Post by: TheExDeus on December 05, 2010, 03:49:12 pm
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.
Title: Re: variable_* Functions
Post by: Josh @ Dreamland on December 05, 2010, 10:03:03 pm
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.
Title: Re: variable_* Functions
Post by: freezway on December 06, 2010, 08:36:31 pm
since enigma allows pointers the *_set_* ones are easy enough to work around. honestly you should really need these
Title: Re: variable_* Functions
Post by: Josh @ Dreamland on December 06, 2010, 10:05:13 pm
...
Title: Re: variable_* Functions
Post by: RetroX on December 07, 2010, 07:23:19 pm
it would be a massive map of pointers, which would be slow as hell
Title: Re: variable_* Functions
Post by: serprex on December 13, 2010, 09:04:15 am
These are better replaced by string indices
Title: Re: variable_* Functions
Post by: luiscubal on December 13, 2010, 07:08:36 pm
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.
Title: Re: variable_* Functions
Post by: RetroX on December 13, 2010, 07:42:09 pm
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.
Title: Re: variable_* Functions
Post by: luiscubal on December 14, 2010, 08:51:09 am
@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:
Code: (C++) [Select]
#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?)
Title: Re: variable_* Functions
Post by: RetroX on December 14, 2010, 06:07:18 pm
@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:
Code: (C++) [Select]
#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.
Title: Re: variable_* Functions
Post by: Rusky on December 15, 2010, 12:46:46 pm
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.
Title: Re: variable_* Functions
Post by: luiscubal on December 15, 2010, 02:06:12 pm
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?
Title: Re: variable_* Functions
Post by: RetroX on December 15, 2010, 04:22:47 pm
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.
Title: Re: variable_* Functions
Post by: luiscubal on December 15, 2010, 05:21:20 pm
I'm getting the POINTER to the variable.
If you are wondering, this is the behavior:
Code: (c++) [Select]
int signal = 0;
...
printf("%d\n", signal); //Prints 0
*GetProcAddress(GetModuleHandle(NULL), "signal")) = 2;
printf("%d\n", signal); //Prints 2
Title: Re: variable_* Functions
Post by: RetroX on December 15, 2010, 08:21:19 pm
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.
Title: Re: variable_* Functions
Post by: Rusky on December 16, 2010, 11:39:19 am
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.
Title: Re: variable_* Functions
Post by: RetroX on December 16, 2010, 03:35:12 pm
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.
Title: Re: variable_* Functions
Post by: Rusky on December 17, 2010, 09:42:20 am
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.