FIFE forums

Please login or register.

Login with username, password and session length
Advanced search  

News:

FIFE 0.4.0 has been released on 15th of January, 2017!

Author Topic: C++ interface  (Read 5197 times)

Dzverot

  • Newbie
  • Posts: 5
    • View Profile
C++ interface
« on: January 21, 2009, 10:34:47 pm »

Hello, I've been playing with FIFE for two days now. I haven't got any experience in game programming and I haven't done anything meaningful with the engine yet. But it does look very interesting.

So what is my post about...
I have a feature request - an interface for using the core fife lib in C++. I'm basically asking for exports for all functions in the dynamic (shared) library ( + a header file).

This way the main application loop can be in C++ code.
This doesn't sound too exciting, but it's going to be something interesting to see.

I have basic idea how to do this in win32 + MSVC. But I don't know how this is going to work with other platforms. I also don't know if something like can be done without breaking portability.
Logged

vtchill

  • Developer
  • Full Member
  • *
  • Posts: 206
    • View Profile
Re: C++ interface
« Reply #1 on: January 22, 2009, 02:42:18 pm »

I am currently working on some c++ tutorials for FIFE to help get people who want to develop in c++ started. I am building FIFE as a static lib and linking my executable directly with it. This is the easiest method to get started with FIFE in c++. I may put some effort later into building a dynamic lib, but that is not my highest priority right now. Also there are a few hurdles that I am working on right now such as a proper map/object loader in c++ that I hope to be able to commit in the near future which will make it much easier for a c++ developer.

I am hoping the tutorials will cover the basics such as how to initialize and setup the engine, load a map file, add mouse/keyboard listeners, zooming, scrolling, simple AI (pathfinding, wandering, steering, flocking, etc). If you or anyone else has any suggestions of  tutorials they would like to see please post them here and I will try and get to them at some point.
Logged

Dzverot

  • Newbie
  • Posts: 5
    • View Profile
Re: C++ interface
« Reply #2 on: January 22, 2009, 05:58:21 pm »

Right now I'm just putting my code in the core library and compiling everything together. It works just fine but it's not the prettiest thing to do :)
Making FIFE a static lib is a good solution. I'll try that. It's definitely going to make compilation much faster. But having it compile as dynamic C++ lib is even better.
Even if this is not high priority - I think it would be a good addition to the engine.

About the tutorials - I actually already got the "initialize and setup" part done. Once I saw how the "rio de hola" client works everything was pretty easy and straight-forward.
Just for a few hours of work I already had a FIFE-powered black window written in C++.

My point is - the included clients can serve as pretty good tutorials on the engine even without further documentation.
However - I'd be happy to see a tutorial on engine extensions (e.g. inheriting from ResourceLoader class).
Logged

vtchill

  • Developer
  • Full Member
  • *
  • Posts: 206
    • View Profile
Re: C++ interface
« Reply #3 on: January 23, 2009, 01:06:05 pm »

Agreed the included tutorials are a nice way to figure out how things are done even if they are in python. I personally think the static lib is pretty nice and very easy to work with. Dynamic libraries have some advantages, but after using them extensively at work I really think they make most things over complicated and cause lots of hard to track down problems. I guess I just don't see a huge benefit over a static linking model for the fife core library. If fife was more modular and spread out with more of a multiple DLL architecture with plug-in support then maybe I could see the advantages. Also we are far from having a good DLL interface with a proper API exported, that will take a good bit of effort and time. I will keep it in mind for the future as I think it would be a nice alternative to have for those who want to link dynamically.

Keep a lookout for the map loader and tutorials I am hoping to get some of it out soon. Also please keep posting about your experience and if you have any of your own tutorials you would like to share please feel free to submit them.
Logged

mvBarracuda

  • Administrator
  • Sr. Member
  • *
  • Posts: 411
    • View Profile
Re: C++ interface
« Reply #4 on: January 23, 2009, 03:58:34 pm »

The advantage of using a dynamic library would be, that you wouldn't need to put your code under the LGPL. With the current licensing policy, statically linking against libfife means that you need to open source all of your code under GPL or LPGL as well.

EDIT: I talked about the whole dynamic library issue with vtchill at the IRC channel. Check out the discussion log here:
http://openanno.org/fifelogs/?action=view&chan=FIFE&date=2009-01-23&mark=287#287
« Last Edit: January 23, 2009, 04:26:18 pm by mvBarracuda »
Logged

Sleek

  • Developer
  • Jr. Member
  • *
  • Posts: 57
    • View Profile
Re: C++ interface
« Reply #5 on: January 23, 2009, 04:46:00 pm »

Dzverot,

How advanced are you with C++ ?

Last time I used gcc and auto-exported all symbols, so I can use fife.dll directly without any major changes.

But the proper thing to do is explained here : http://mirror1.cvsdude.com/trac/fife/engine/ticket/330 . Perhaps you can help us with that ?

I had a basic maploader in C++, it was fun for a while, for the loading speed. But the code was 90% ported from our python maploader. For me, it's easier and faster to use python for prototyping your game. It has native support for more than basic data structures, and it doesn't need recompilation. Advantage of C++ is speed.

If you are just playing around with FIFE and C++, without production code ( haha I wish ), static or dynamic doesn't matter. If you don't distribute your game, you don't have to publish your source code. And if you do want to distribute it, should be no problem disclosing the source since it's a play-test code right ? You stated that you have had no experience in game development, so I think you would need to show your code to people for advice quite a bit.

I advise you to learn python if you have no idea what it is, it's worth it :)
Logged

Dzverot

  • Newbie
  • Posts: 5
    • View Profile
Re: C++ interface
« Reply #6 on: January 24, 2009, 03:38:52 pm »

Sleek is right here. I'm only playing around without any production code. It's really not that important if I'm using static or dynamic linking.

However dynamic linking has the advantage mvBarracuda pointed out.
Also - with current size of FIFE (~ 5MB for me) if there are multiple executables (e.g. actual game, map editor, model editor etc.) it's better to have one DLL.

@Sleek: I have experience in both Python and C++. And Python is easily my favorite object oriented language. But the reason I'm trying to use FIFE in C++ is the one you pointed out - speed :)

Quote from: Sleek
Perhaps you can help us with that ?
I had no idea that gcc auto-exports all symbols. This means that I can try to add MSVC export code only and everything will work just fine.
I'll try to put something together and let you know if I could do it.


Edit: I've got it working. I've written a python tool that will change the headers so that they export their classes. It's slightly buggy and the header I've written is oblivious of anything but MSVC. I'll clean it up a little, I'll add some comments and post again.

Here's what I've done so far:

Code: ("CreateExports.py") [Select]
import os
import re

header_extensions = ["h", "hpp"]

decl_spec_header = '#include "declspec.h"\n'

decl_spec_re = "\s*class\s*.*{.*" # I hope everyone is ising the same coding convention...
decl_spec_string_before = "class"
decl_spec_string_after = "class CLASS_DECLSPEC"

headers_found = set()

def ProcessHeader(filename):

    #create backup of the header file
    filename_new = filename + '.bak'
    if os.path.isfile(filename_new):
        os.unlink(filename_new)
    os.rename(filename, filename_new)

    file_out = open(filename, 'w')

    add_include_common = False
    add_declspec = False
   
    for line in open(filename_new, 'r'):

        # add include for the header with the export macro
        if line.startswith("// FIFE includes"):
            add_include_common = True
        if line.startswith(decl_spec_header):
            add_include_common = False
        if not line.startswith("//") and add_include_common:
            add_include_common = False
            file_out.write(decl_spec_header)

        # add the export macro to class declaration
        if re.match(decl_spec_re, line) and line.find(decl_spec_string_after) < 0:
            line = line.replace(decl_spec_string_before, decl_spec_string_after)
       
        file_out.write(line)

def ProcessDirectory(dirname):
    for dirpath, dirnames, filenames in os.walk(dirname):
        for filename in filenames:
            filename_extension = filename[filename.rfind(".")+1 : ]
            if filename_extension in header_extensions:
                filename_full = os.path.join(dirpath, filename)
                print "Processing header file:"
                print filename_full
                ProcessHeader(filename_full)
                headers_found.add(filename_full)
                print ""

def CreateImportHeader(dirname, filename):
    import_header = open(os.path.join(dirname, filename), 'w')
    for h in headers_found:
        import_header.write('#include "%s"\n' % h.replace(dirname, ""))

ProcessDirectory("engine/core/")
CreateImportHeader("engine/core/", "import_header.h")

Code: ("declspec.h") [Select]
#ifdef WIN32
#ifdef _LIB
#define CLASS_DECLSPEC __declspec(dllexport)
#else
#define CLASS_DECLSPEC __declspec(dllimport)
#endif
#else
#define CLASS_DECLSPEC
#endif

How-to-use: Create a backup of the "<FIFE>/engine/core/" folder. Copy the python script to the "<FIFE>/" folder and execute. The header should go to the "<FIFE>/engine/core/" folder.
« Last Edit: January 24, 2009, 09:39:06 pm by Dzverot »
Logged

mvBarracuda

  • Administrator
  • Sr. Member
  • *
  • Posts: 411
    • View Profile
Re: C++ interface
« Reply #7 on: January 25, 2009, 05:21:45 pm »

Looks like you're making progress in the field Dzverot :-)

One note about editing previous posts: don't hesitate to create a new post even if you were the last replier to the thread. It's easier to not miss any kind of new information and you don't sound like a kid who doubleposts without valid reason.
Logged

Dzverot

  • Newbie
  • Posts: 5
    • View Profile
Re: C++ interface
« Reply #8 on: February 01, 2009, 11:15:42 am »

I'm pretty much done.
This is not tested with anything but MSVC 2008. I think it turned out to be too messy to be included in the trunk. But anyway...

Put this file in <FIFE>\engine\core
Code: ("declspec.h") [Select]
#ifdef WIN32
#ifdef _LIB
#define CLASS_DECLSPEC __declspec(dllexport)
#else
#define CLASS_DECLSPEC __declspec(dllimport)
#endif
#else
#define CLASS_DECLSPEC
#endif

Then put this in the main FIFE folder, read the instructions and execute it. After that everything will be ready for dynamic linking in MSVC.
Code: ("CreateExports.py") [Select]
# How to use:
# Create a back up of "<FIFE>\engine\core\"
# place this file in main FIFE folder and execute it
# This will add export statement to all classes it detects in the header files
# It will also create a file "import_header.h" in the "<FIFE>\engine\core\" directory which includes all header files

import os
import re

header_extensions = ["h", "hpp"]
decl_spec_header = '#include "declspec.h"\n'

# finding classes:
decl_spec_re = "\s*class\s*.*{.*" # I hope everyone is using the same coding convention...
template_class_re = "\s*template.*"

# replacement:
decl_spec_string_before = "class" # text to replace:
decl_spec_string_after = "class CLASS_DECLSPEC" # text to replace with

headers_found = set()

def ProcessHeader(filename):
    """Adds a declspec macro to the classes in the given header file"""

    #create backup of the header file
    filename_new = filename + '.bak'
    if os.path.isfile(filename_new):
        os.unlink(filename_new)
    os.rename(filename, filename_new)

    add_include_common = False
    is_prev_line_template = False

    with open(filename, 'w') as file_out:
      with open(filename_new, 'r') as file_in:
        for line in file_in:

            # add include for the header with the export macro
            if line.startswith("// FIFE includes"):
                add_include_common = True
            if line.startswith(decl_spec_header):
                add_include_common = False
            if not line.startswith("//") and add_include_common:
                add_include_common = False
                file_out.write(decl_spec_header)

            # add the export macro to class declarations
            if re.match(decl_spec_re, line) and line.find(decl_spec_string_after) < 0 and not is_prev_line_template:
                line = line.replace(decl_spec_string_before, decl_spec_string_after)
            if re.match(template_class_re, line):
                is_prev_line_template = True
            else:
                is_prev_line_template = False

            # write the line
            file_out.write(line)

def ProcessDirectory(dirname):
    """Adds a declspec macros to the the headers in the given folder (and its subfolders)"""
    for dirpath, dirnames, filenames in os.walk(dirname):
        for filename in filenames:
            filename_extension = filename[filename.rfind(".")+1 : ]
            if filename_extension in header_extensions:
                filename_full = os.path.join(dirpath, filename)
                print "Processing header file:"
                print filename_full
                ProcessHeader(filename_full)
                headers_found.add(filename_full)
                print ""

def CreateImportHeader(dirname, filename):
    """Creates a header that contains all headers found up to now"""
    import_header = open(os.path.join(dirname, filename), 'w')
    import_header.write(decl_spec_header)
    for h in headers_found:
        import_header.write('#include "%s"\n' % h.replace(dirname, ""))

ProcessDirectory("engine/core/")
CreateImportHeader("engine/core/", "import_header.h")
Logged

Sleek

  • Developer
  • Jr. Member
  • *
  • Posts: 57
    • View Profile
Re: C++ interface
« Reply #9 on: February 02, 2009, 02:35:06 am »

Dzverot, care to come by our IRC channel ?

#fife @ QuakeNet

I'll try to get on around 12:00 noon GMT
Logged

Dzverot

  • Newbie
  • Posts: 5
    • View Profile
Re: C++ interface
« Reply #10 on: February 02, 2009, 05:37:12 pm »

@Sleek: I couldn't be in IRC in 12:00. But some time later I joined #fife and I found Barracuda there. He told me you're interested in using this.

We've talked and he asked me to make a patch with the changes. I did that (see attachment).
I've also included some fixes I've done manually after I ran the python script.
Logged