🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

I am using ugly ugly hacks.

Started by
6 comments, last by Deyja 17 years, 9 months ago
Lots of them. I was hoping something could be done to make them unecessary. First, would it be possible to normalize the interface for setting arguments of primitive and object type? I understand how seperate setting functions for primitive types can foster some speed increase, but it would be nice if the version that takes a void* worked for primitives as well. I'm burning any possible optimization figuring out which function to call to do the setting. The same is true for return values. I'd like to be able to get a void pointer to it, no matter what type it is. (Please feel free to slap me if get_return_object and set_argument_object already work like this) The other is built in keywords. I'd actually like to be able to change them. I can change the names of the built in types, but I'd actually like to change the 'this' keyword and the name of the built-in 'any' type. The name of the any seems to be tossed all over as_anyobject.cpp. Is it anywhere else? The 'this' keyword does not appear in as_tokendef.h with the rest of the reserved words. Where is it? (Speaking of as_tokendef.h, please consider restoring the struct keyword to non-deprecated status. In C++, it's an alias for class - can't it serve the same function in AngelScript?) You're probably wondering why I want to change keywords. Well, I'm trying to get a very specific syntax in my scripts. They aren't really 'script files'; they are inlined right into an XML entity definition file. The function's headers are generated. They aren't members of a class, but they behave as if the entire file represented a single class. They even receive an invisible this parameter - and there's the rub. I'm actually using my preprocessor to define 'this' to something else. This unfortunatly makes it impossible to use the real 'this' keyword. The only instance of 'this' I can seem to find is line 4032 of as_compiler.cpp. I did a search with the regex '".*this.*"', to find string literals that contain the word this. I don't know enough about how your code works to be confident enough to change it there and expect it to work how I want. Here is the ugly code I am using to set an argument.

bool sil::c_FunctionCall::set_argument(int a_id, sil::Any value)
{
	if (!ctx) return false;
	if (state != SIT_READY) return false;
	int r_code = -1;
	if (value.is_of_type<char>())
		r_code = ctx->SetArgDWord(a_id,value.reference<char>());
	else if (value.is_of_type<short>())
		r_code = ctx->SetArgDWord(a_id,value.reference<short>());
	else if (value.is_of_type<int>())
		r_code = ctx->SetArgDWord(a_id,value.reference<int>());
	else if (value.is_of_type<unsigned char>())
		r_code = ctx->SetArgDWord(a_id,value.reference<unsigned char>());
	else if (value.is_of_type<unsigned short>())
		r_code = ctx->SetArgDWord(a_id,value.reference<unsigned short>());
	else if (value.is_of_type<unsigned int>())
		r_code = ctx->SetArgDWord(a_id,value.reference<unsigned int>());
	else if (value.is_of_type<bool>())
		r_code = ctx->SetArgDWord(a_id,value.reference<bool>());
	else if (value.is_of_type<float>())
		r_code = ctx->SetArgFloat(a_id,value.reference<float>());
	else if (value.is_of_type<double>())
		r_code = ctx->SetArgDouble(a_id,value.reference<double>());
	else
		r_code = ctx->SetArgObject(a_id, value.void_pointer());

	if (r_code < 0) { state = SIT_ERROR; return false; }
	return true;
}		


I never understood why this was neccessary when I can set globals so generically using GetGlobalVarPointer.
Advertisement
You have some valid points. Thanks for bringing this up.

We C++ developers like to trust what we do, so there's really no need to have the interface force us to use separate functions for type correctness. I'll see if I can expose a generic method, something like GetArgPointer() which returns a void* to the argument value. The same for the return value.

The 'this' and 'any' keywords aren't special tokens, they are special identifiers. Just like the 'from' keyword in the import statement. That's why you didn't find them where the other tokens are. However, for the sake of better maintenance I'll define these constants in the as_tokendef.h file as well (though separately from the other tokens).

I actually have a different plan for the 'struct' keyword, though no firm decision has been taken yet. I want AngelScript to be able to declare C++ compatible data structures, and I was thinking of using the keyword struct to do this. The script class has a lot of overhead, with the reference counter, object type identifier and all. But in order to have a C++ compatible data structure there can be absolutely no overhead. These structures would of course not be reference counted, so the object handle wouldn't be supported for them (unless I change the memory management model to something else than reference counting).

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

In case your interested, what I've got going now is a sort of dynamic type system in C++. If you register a type with AngelScript through my library, you can do all sorts of things with it, such as construct an object of that type using the name. Now, C++ doesn't actually have dynamic typing (You knew that, duh. :) ) so I use an Any class. It's descended from that one I sent you ages ago. Right now, I can call an angelscript function totally genericly. I need the function name, in a string, an Any to catch the return value, and a list of Anys for parameters. It builds the function signature, looks it up, sets arguments, and catches the return value, all without hardcoding a type.

Unfortunatly, you can't really use the stuff in C++ code unless you know the type somewhere, though you can convert to any registered type with the name as a string, the conversion isn't always meaningful, and you still need to know the actual type that name corrosponds to to make the jump into C++ code. So, I haven't done anything to support types declared inside AngelScript making the jump back out. I just don't think that's going to be possible.

So, I wanted to call my Any class 'any' inside AngelScript, so that it matches the names of the builtins, but the name was already taken. I just want to change the builtin name to avoid anyone using it accidentally.
Perhaps I should specify what behavior I actually want.
It would have the signature 'error_code SetArg(arg_id, void*)'.
It would work for any type, or reference. If the parameter was received by reference, it would just use that void*. If it was received by value, it would dereference the void* and copy it. Yes, I know then that it would be -MY- responsibility to make sure whatever that void* pointed to would remain in existence until the script completes. (Incidentally, using that void* directly as the reference allows script functions to modify variables in the C++ code directly. It is my present understanding that references are currently copied anyway)
Similarily, the return value would be available as a void* for all types. And it would be -MY- responsibility to copy whatever that void* pointed to before I recycled the context and destroyed the return value.

I'm having another issue at the moment, though. I'm looking up functions by building their signature using the return type and the parameter types. Unfortunatly, this means I can't use const or & parameters because GetFuncByDecl expects the exact declaration (understandably). I can't generate 'const' or '&' in the code that builds the signature because the calling code doesn't know, nor care, how the callee function receives it's parameters.

The behavior I want from function selection is analogous to overload resolution. Given the function name, and the type of each parameter, I want to choose the best possible match. In my case, the type of the parameter's never includes const or reference (Const is explicitly removed, and reference is impossible because it would form references to references in the library code). This isn't so hard to implement myself. I can enumerate the functions in the module, parse their signatures, and perform overload resolution on them myself. However, that's a lot of work - especially when you've already done the exact same thing in the library. Unfortunatly, I need slightly different overload resolution behavior than is allowed in-script. (Because types in the layer between C++ and the script are dynamic, overload resolution can convert types however it wants - the function with the least conversions neccessary wins) It's the parsing I'm actually worried about. I only need worry about const and & (handles have been banished from my scripts forever) but future changes to the library can break my parser quite badly. That out of the way... do you have an idea of how far away from the next stable release you are? I have the feeling that the sooner I can 'lock' a version into the project, and stop worrying about upgrading it, the better. :D

Right now, I'm working around these issues by banishing const and & from script function signatures, and by specifying the return type on the C++ side.
I'll see what I can do.

Ultimately I actually want to remove the built-in any class, and make it an externally registered class, just as the string type. But for now it requires some ugly hacks inside the compiler to work, so that will only happen when I can remove the hacks.

For now, you should be able to simply rename the any class, or even remove the internal registration of it that is done when creating the engine.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Uglier hack that just might work:
#define this SOMETHING#define _this this

If you're compiler reads that way, it'd convert the lexical this's to whatever, and you're other _this to the actual this.

Just a thought?
We should do this the Microsoft way: "WAHOOOO!!! IT COMPILES! SHIP IT!"
That's actually exactly what I'm doing. Since the signatures of the functions taking the 'this' parameter are all the same and standardized, I've just got a macro '#define this __THIS' (Please notice the implementation-reserved replacement...) Since my preprocessor lets me #define things from the C++-side, the scripts don't ever need to know about the change.

I've got function overload resolution working (And, it seems, primitive types by reference using that SetArgPointer function) Const parameters are ignored by overload resolution, as they effect the argument setting code in no way whatsoever. My overload rules are somewhat odd, though. At the moment, I allow conversions from any type to any other type (Though I might change that) so any function with the right number of arguments could match. The rules are simple: Whichever signature has the most matching types wins. If two functions have the same number of matching parameters, it's ambiquous. Additionally, it's impossible to convert to match a reference parameter. It has to be the same type, or the match fails. This is because, if I allowed the conversion, the script function would be modifying a copy. (Actually, I still need to ensure the lifetimes of reference parameters sent into the script) I might allow conversions to const&, as they won't be modifying anyway.
The addition of 'conversion' behaviors may allow the removal of ugly hacks. Behaviors could be registered to convert types to the any type to allow passing into functions. Otherwise, an outside-bound any has to make due with free functions (what I use) or constructors and assignment behaviors. As I understood it, your intent with the Any type was to allow it to hold script-declared classes. That may be impossible without those ugly hacks...

This topic is closed to new replies.

Advertisement