|
||||||||||||||||||
Add Forum to Favorites | Send Topic To a Friend | View Forum FAQ | Track this topic |
Last Thread Next Thread ![]() |
| Creating a Scripting System in C++ Part III: Dynamic Loading |
|
![]() octura Member since: 3/13/2002 |
||||
|
|
||||
| Great article, I, however, still have a question in which direction you are going to take the VM and especially the compiler. So here is the question: Will the OPCODE be 1)very basic stack based commands (CALL,ADD,POP...) or 2)game engine specific commands (DISPLAY MESSAGE, PLAY ANIMATION...) If it is the second choice than I would like to know such a compiler would look like? The think is I totally agree with using bigger OPCODES don't understand yet how to write a compiler to make them. Thanks Markus BTW: I do think your articles are one of the best in the game community on this subject. |
||||
|
||||
![]() Redleaf Member since: 5/31/2000 From: Rochester, NY, United States |
||||
|
|
||||
| I will be introducing opcodes which are more stack-based in the next article. However, realize that nothing I do should be taken as law written in stone as to how things should be done. For your own projects, you may be best off using very specific opcodes as they relate to your game engine. For those who need more complexity, I am introducing possibilities for achieving it in my writing. "Don't be afraid to dream, for out of such fragile things come miracles." |
||||
|
||||
![]() Wreakon Member since: 7/14/2001 From: Washington, USA |
||||
|
|
||||
| Hey, great article, I'm not writing a scripting engine *yet*, but it let's me look at how not to make mistakes when I do it ------- Happy Coding! Homepage: www.ussshrike.com/pcwebvia/ |
||||
|
||||
![]() Larsen Member since: 5/20/2001 From: Gislaved, Sweden |
||||
|
|
||||
Hi, Thank you for a very educational tutorial. I find this information very useful in my daily job, where I have a instant need for simple changes/updates to applications. I'm used to take advantage of custom .ini files, but they seems to not satisfy more than about 10% of neccesary enhancments. Scripting seems to me like the best solution for my instantly growing problem. Thank you again! |
||||
|
||||
![]() VizOne Member since: 10/4/2001 |
||||
|
|
||||
void operator=(const Buffer& buf)
{
delete[] _data;
_size = buf._size;
_data = new char[_size];
memcpy(_data, buf._data, _size);
}
Be sure to check for self-assignment if operator= deals with heap data. Imagine "this" and buf were the same, then you would actually delete the heap-data of buf and then perform a memcpy on an empty data block. Bad idea... Bye, VizOne [edited by - VizOne on March 17, 2002 8:12:12 AM] |
||||
|
||||
![]() Redleaf Member since: 5/31/2000 From: Rochester, NY, United States |
||||
|
|
||||
void operator=(const Buffer& buf)
{
if (&buf == this)
return;
delete[] _data;
_size = buf._size;
_data = new char[_size];
memcpy(_data, buf._data, _size);
} "Don't be afraid to dream, for out of such fragile things come miracles." |
||||
|
||||
![]() VizOne Member since: 10/4/2001 |
||||
|
|
||||
quote: Well, better, but a operator = should return a self-reference (to make operations chainable), shouldn't it? Buffer & operator=(const Buffer& buf)
{
if (&buf != this)
{
delete[] _data;
_size = buf._size;
_data = new char[_size];
memcpy(_data, buf._data, _size);
}
return *this;
} I guess in the context you use Buffer, it is not neccessary to implement that. Nevertheless it's more consequent to do it. Bye, VizOne [edited by - VizOne on March 18, 2002 8:58:26 AM] |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
| I've just read your articles and they good overall. Well done on completing them. However, I do have some queries about your coding. You might feel you should be allowed to code how you want, but because this is an article for people to learn from, you should try to show the best example you can. In the "Cleaning Up" section, your Buffer object is just a reimplementation of part of std::vector<char>. I could understand you wanting to limit the interface but you shouldn't be reimplementing it. Also, in the "Basic File Handling" section, you then go on to create file objects which just reimplement part of std::ifstream and std::ofstream. It looks like your just creating work for yourself. You also get some input from the keyboard using a fixed sized buffer (after implementating a dynamic sized buffer). This is much more robust: std::string input std::cin >> input; I am just very confused why you use C++ features, such as std::cin, std::vector, and then mix them up with C features, such as handling your own container memory and FILE, when they are much less robust and more error prone. I would prefer if you would use the solutions that are already in the standard library: it is better practice, your code would be more concise and you could progress quicker. Also, you are using a leading underscore for member variables, which doesn't conform to standard C++ as these names are reserved for the compiler. |
||||
|
||||
![]() Redleaf Member since: 5/31/2000 From: Rochester, NY, United States |
||||
|
|
||||
| I suggest you read that part of the standard again: [lib.global.names] 17.4.3.1.2 Global names Certain sets of names and function signatures are always reserved to the implementation: — Each name that contains a double underscore (__) or begins with an underscore followed by an uppercase letter (2.11) is reserved to the implementation for any use. — Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace. Member variables are certainly not part of the global namespace. As to buffer being a reimplementation of vector, it's certainly not. It's quite a bit more simplistic. quote: That's not going to grab an entire line if there are spaces. I think I explained my choice as to files. "Don't be afraid to dream, for out of such fragile things come miracles." |
||||
|
||||
![]() Sturm Member since: 4/21/2001 |
||||
|
|
||||
| Just to add to the war of "right" coding Not that I'm in any way am coding to any standard, I use whatever is right in the situation I'm standing at. This might mean I use fullblown OOP model, or totally shift over to asm and do it there (Doing it fast, not "right"). The point here for me is that Redleaf is trying to show ppl who to create a scripting engine, not teach them how to program, it's expected that the reader can do that. As to quote him from the first article quote: He also states the this will NOT be an OO engine, but only use C++ classes for the reason that they are there, quote: quote: I guess that's the reason Redleaf leans towards C and not so much C++. Looking from a newbies perspective I would guess that implementing your own file handling rutine will help you understand the i/ostream parts. As I started with this is a script engine learning, not coding. I for that matter, do not use much of the code from here directly but do my own implementation of it, this implementation is pure OO and does use all the new nice features of C++ and VS.NET, but this certainly doesn't mean that my code is better, it's just different So give the man some slack and let's focus on the scripting engine and if you want to worry about coding standard do that in your own code. BTW. Nice set of articles --------------------------------------------------- Life after death? No thanks, I want to live NOW --- Sturm 2001 |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
quote: I don't know where you got that quote because there are additional things that you have missed out such as them also being reserved in ::std. In all coding standards I have ever read, they say you should not use underscores like this. There is also an issue with compilers that also reserve many identifiers starting with an underscore regardless of scope. The practise is simply frown upon. quote: There is no reason to expose yourself to creating and deleting memory, buffer overruns and destructors, assignment operators and copy constructors. How can you possibly say it is more simplistic than this?
class Buffer
{
public:
Buffer() { }
Buffer(const char* d) : data(d) { }
const char* Data() const { return data.c_str(); }
private:
std::string data;
};
If you complain about data.c_str() being slow or something, you should really be using iterators. It just seems like you've made a redudant class that has no purpose. quote: Use this then: std::string string; std::getline(std::cin, string, '\n'); quote: I really don't understand why you made that choice. You are learning C++ which has OO file streams already. Instead of learning and using them, you take the longer option of wrapping up the C ones in a class. You also name your class with methods similar to the C++ ones, which makes me think you know about them anyway. The C++ ones are much more elegant and safe. I wouldn't be making these comments if you looked like a useless programmer. You seem to have a good knowledge of C++ practice (e.g. inheritence, correct casting, STL containers), I just really don't understand why you mix it up with C code that is much less robust. Also, you say these things make your article more simple but that doesn't stop them taking up about half of it. You should be striving to improve these things and not making excuses. It would improve the quality and educational value of the article vastly. |
||||
|
||||
![]() Digitalfiend Member since: 3/21/2000 From: Toronto, Canada |
||||
|
|
||||
| AP, You do realise that there are incompatible implementations of the STL, right? Some compilers can't even handle templates properly. There will always be a use for custom classes, even if they appear to reimplement what has already been done. I do agree with you about the leading underscore. I used to do that and ran into problems on a project. Now I use trailing underscores: _somedata; somedata_; // better Still, I don't see the point on picking his code apart. He is attempting to convey a relatively complex topic. Dire Wolf www.digitalfiends.com |
||||
|
||||
![]() Redleaf Member since: 5/31/2000 From: Rochester, NY, United States |
||||
|
|
||||
| Your points are heard, AP, and noted. I'll keep you in mind as I write the next article. As to the reference regarding underscores, that comes from the C++ Standard. Bartosz Milewski from Relisoft pointed that out to me when I asked him about the same thing, in case you were wondering. "Don't be afraid to dream, for out of such fragile things come miracles." [edited by - Redleaf on March 19, 2002 7:27:16 PM] |
||||
|
||||
![]() ggs Member since: 1/2/2000 |
||||
|
|
||||
| Good article. The topic of loading your scripts is definally important. I'm looking forward to see how you have handled the stack. In the VM I've been designing/writing each variable has: BeginRead,EndRead,BeginWrite,EndWrite This allows be to directly access the internal structure of the varaible (supports all the standard primative datatypes), and allows the stackpointer to be used as a peudo-variable. IE when you read from it, it pop's an item off the stack, when you write to it, it pushes the result onto the stack. Also this would allow multipule scripts to run co-currently, as each instruction is atomic to the script. IE c := a+b, 1 op Since reading the article I'm planning to allow my Instruction classes to read asm code, as well as bytecode. Since I plan to allow the compiler to parse asm, it would be simplistic to move the asm parsing code into the loading code for an Instruction I'm very interested in these articles, as for the debate on naming, I'm writting my compiler and VM in Object pascal for Delphi5. Thus as long as I can follow the concepts I can translate them into my language of choice. |
||||
|
||||
![]() ggs Member since: 1/2/2000 |
||||
|
|
||||
| As of yet you haven't handled control structures. Given that this is a high level script, are you going to resort to using goto? or biuld control constructs directly into the scripting bytecode. Are you going to use flat bytecode (intel,java, etc) or a tree-like structure for your instructions?
IE
if (condition)
{
(* Statements*)
}
else
{
(* Statements*)
}
Translates into :
if
- condition
- true path statements
- false path statements
were "-" indicates a child instruction.
I personally feel that the tree-like structure is ideal for scripts. This makes the implemenation of break,exit,continue and control construct simplistic. As all they have to do is walk the tree of parent instruction to find the place to jump to. Procedures would also be simlified, as you can create a special instruction (ie OP_EntryPoint) which owns its code, and could contain useful debug information. On the note of debugging, scripting are normally sadly lacking in this regard. Dispite the fact it is trivial to include information into the bytecode (and it would be handled at load-time and not run-time). I think it would be a good idea of implementing some debugging tools for the VM. |
||||
|
||||
![]() Redleaf Member since: 5/31/2000 From: Rochester, NY, United States |
||||
|
|
||||
| The types of instructions I will be providing as example in the next article will not really be too high level at all. For all intents and purposes, higher level control structures will be handled through patterns of simplistic jump codes. That doesn't mean control flow can't be handled the way you present it, but I feel a flat structure is easier to model for the sake of this series. As for debugging, I do intend to include additional facilities for this as they become apparent. Please feel free to share any ideas regarding this, as I could always use some more. "Don't be afraid to dream, for out of such fragile things come miracles." |
||||
|
||||
![]() possum007 Member since: 3/26/2002 From: USA |
||||
|
|
||||
| I read all three articles in the series back to back. Oh boy it gave me a brain aneurism. :-) I've been working on my scripting engine and I read your article for ideas. I know people have been extremly critical of your programming style so far, so I apologize up front. I'm not much of a C++ person, but it seems to me that all your code snippets are classes. Please correct me if I'm wrong but I thought classes where the domain of C++ and not C? Anyways, my implimentation is in visual basic (ok, laugh all you want), and I started off with the lexical analyzer and was about to procede to creating a parser for the script, and then I realized I had no clue which method of design for a virtual machine would suit my needs. So today I wrote and tested 3 different implimentations of a virtual machine, and each one hardly had any code at all in it. My point is that I don't really understand why you have all the code that you do. Perhaps some pseudo code would be better so people can understand concepts instead of worrying about a silly underscore. I get stumped on lines like code Code _code; that's some scary stuff. |
||||
|
||||
![]() Redleaf Member since: 5/31/2000 From: Rochester, NY, United States |
||||
|
|
||||
| Well, I really don't mean to be rude or anything, so keep that in mind as you read my suggestions. First of all, if you read the title of the article series, it says "Creating a Scripting System in C++." I never said I was using straight C. The line that stumped you I'm guessing is found in the Instruction class. "opcode _code;" This is simple variable declaration of a type declared by enumeration. For the sake of clarity, it is basically a typedef of an int. If something like this stumps you, you really should be reading more introductory material on the programming language first, before reading any more specific articles, like this one. This series isn't meant to teach you a language. I do try to explain the concepts as I go along with the source code snippets. Perhaps you'd like to share with me a specific example where it doesn't seem I explain well enough? Then it would be much easier for me to clear up the misunderstanding. "Don't be afraid to dream, for out of such fragile things come miracles." |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
I agree with the other Anonymous Poster that there's too much "reinventing the wheels" here. The Buffer is just a vector<char> and the File classes just wraps the C I/O library into C++... which the iostream library already does. It's as if the author has nothing better to do than to rewrite rigorously tested code better known as the Standard Template Library. |
||||
|
||||
![]() Redleaf Member since: 5/31/2000 From: Rochester, NY, United States |
||||
|
|
||||
| Well no, actually. The fstream classes do not implement typesafe manipulation of binary files. A class would have to be created to deal with that whether or not I chose C or C++ file IO. If you would like to work with the typeless read() and write() of the fstream, be my guest. And the buffer is not always intended to simply be a character array. Explicitly creating a class to represent the buffer forces the reader to understand exactly how the data is internally represented so that they can knowingly make use of it to store any relevent data necessary for whatever instruction types they decide to create. Creating the buffer was a matter of illustration, and I gladly welcome anybody to replace it with whatever they choose. "Don't be afraid to dream, for out of such fragile things come miracles." |
||||
|
||||
All times are ET (US)![]() |
Last Thread Next Thread ![]() |
|