Page 1 of 2 12 LastLast
Results 1 to 10 of 12

Thread: [opinion][Research] API design

  1. #1

    Question [opinion][Research] API design

    So, I’m rewriting my engine and I’m struggling with API names.

    I was using prefixes and suffixes to differentiate the engine and subsystems stuff. For example, this program for an hypothetical action game:
    Code:
    program Game;
    
      uses
        Mingro, mngSprites, Title, Playfield, sysutils;
    
      function Initialize: Boolean;
      begin
        if mngInit and mngInitializeSprites then
        begin
          PlayerSpriteSheet := mngLoadSpriteSheet ('player.spr');
          if PlayerSpriteSheet = Nil then
            Exit (True)
          else begin
            mngLog.Trace (etError, 'Can''t load sprite sheet.');
            Exit (False)
          end
        end
        else begin
          mngLog.Trace (etError, 'Can''t init engine!');
          Exit (False)
        end
      end;
    
    begin
      if Initialize then while RunTitle do RunGame
    end.
    What I was thinking is to use the unit name as namespace, so the previous program will look like this:

    Code:
    program Game;
    
      uses
        Mingro, mngSprites, Title, Playfield, sysutils;
    
      function Initialize: Boolean;
      begin
        if Mingro.Initialize and mngSprites.Initialize then
        begin
          Playfield.PlayerSheet := mngSprites.Load ('player.spr');
          if Playfield.PlayerSheet = Nil then
            Exit (True)
          else begin
            Mingro.Log.Trace (etError, 'Can''t load sprite sheet.');
            Exit (False)
          end
        end
        else begin
          Mingro.Log.Trace (etError, 'Can''t init engine!');
          Exit (False)
        end
      end;
    
    begin
      if Initialize then while Title.Run do Playfield.Run
    end.
    I don’t see that kind of API anywhere but I think it is clean and clear and more appealing than using prefix/sufix to differentiate between subsystems (except the fact that when two units declare an object with same name and you don’t precede it with the unit name, FPC doesn’t warns and picks one resulting in some funny debug sessions).

    What do you think about this kind of API?
    No signature provided yet.

  2. #2
    It is hard to say without having access to your entire codebase.

    If you have each Subsystem in separate unit then your second code example actually seems more intuitive as you get clear information on which unit has defined certain object type. It also gives you ability to have different units for different platforms.

    But if you ever decide to put your game engine into a single package I don't think such approach would be quite suitable. I'm not even sure if this would even work if built into a single package.

  3. #3
    PGD Community Manager AthenaOfDelphi's Avatar
    Join Date
    Dec 2004
    Location
    South Wales, UK
    Posts
    1,245
    Blog Entries
    2
    Naming conventions... ARGH!

    I would say do whatever makes it work for you. We adapt to the different naming/organisation conventions and I personally don't really have a preference. I've tried numerous approaches and concluded some time in the future that they were all clunky or less than optimal. Now I just do what feels right at the time, although I don't publish a lot of code.
    :: AthenaOfDelphi :: My Blog :: My Software ::

  4. #4
    Quote Originally Posted by SilverWarior View Post
    If you have each Subsystem in separate unit then your second code example actually seems more intuitive as you get clear information on which unit has defined certain object type. It also gives you ability to have different units for different platforms.
    That's just why I asked. Since I put each Subsystem in separate unit I feel it is natural to use the unit name instead of prefixes and suffixes. I just wonder why it isn't used a lot.

    Quote Originally Posted by AthenaOfDelphi View Post
    Naming conventions... ARGH!
    Yeah.

    Quote Originally Posted by AthenaOfDelphi View Post
    I would say do whatever makes it work for you. We adapt to the different naming/organisation conventions and I personally don't really have a preference. I've tried numerous approaches and concluded some time in the future that they were all clunky or less than optimal. Now I just do what feels right at the time, although I don't publish a lot of code.
    That's my fear: What if it is clunky. I know it may be less than optimal since you have to write more, and it would be prone to mistakes if calling a procedure that has the name duplicated in several units without the unit name (i.e. the "Init" method). If there were a way to force to write the unit name...
    No signature provided yet.

  5. #5
    After thinking some more on this topic I must admit that I'm becoming less and less inclined to your second approach of using using unit names instead of prefixes. Why?
    As you pointed out yourself calling certain method that is implemented in multiple units with same name without providing unit name will lead to unexpected behavior. I'm not sure about FPC but in case of Delphi I believe the method from the unit that was last loaded would be called in such scenario.
    Another disadvantage of such approach is that you are basically forcing yourself to have each system in its own unit. While often this is perfectly fine there are times were you might want to have several connected systems in a single unit in order to improve code readability.
    Wouldn't this also mean that you will have to ad every unit into uses section of your main application unit?
    Also having bunch of small units could have significant affect to build time. Now wile Object Pascal compilers are know to have very fast compile times due the fact that only modified code is recompiled having bunch of small units could still lead to long build times. This mostly due the fact that linker still needs to link a bunch of small compiled code pieces together in order to build final application.

  6. #6
    You have some points here.

    I was coding some time now and besides the build time it seems nice to me. Thankfully Lazarus is able to rename all references by itself, or I can use a tool I wrote some time ago to help with Allegro.pas development, so if I change my mind I can change all identifiers. It isn't TRUNK yet.
    No signature provided yet.

  7. #7
    Real life strikes back.

    I was working on the disk access API, and I've found some of the issues you, SilverWarrior, were talking about. Of course, using the unit name fixes all problems, but I spend some valuable time wondering if TStream should be the original Classes.TStream or the one I extended...

    So I've decided to back to the prefix. It was a nice experiment though.
    No signature provided yet.

  8. #8
    Quote Originally Posted by Ñuño Martínez View Post
    but I spend some valuable time wondering if TStream should be the original Classes.TStream or the one I extended...
    One piece of advice. If you are extending some class with new functionality it is recommended to change the name of the extended class in order to avoid confusing it with the original class that it was extended from.
    That is unless you want your extended class to be used as a base class for other classes that have been extended from the original class and thus in a way inject additional functionality to all of them.

  9. #9
    Maybe this:
    <Action><Object><Attribute/State>();// Do an Action over some Object Attribute/State
    This is how I tend to make my APIs, more info about this here:
    raylib syntax analysis · raysan5/raylib Wiki (github.com)

  10. #10
    The second one BUT use classes with class (static) methods instead of unit names.
    This way you *cannot* call method without specifying which.
    And can pack several ones in the same unit
    And move them between units if you need to change architecture -- without touching the calling code

    in my engine:
    GAPI.TexImage2d(p, format)

    where
    class function GAPI.TexImage2d(
    image: pointer;
    format: TGAPITextureFormat;
    level: GLuint = 0;
    width: GLuint = 0; // 0 = get from image
    height: GLuint = 0; // 0 = get from image
    CreateTextureObject: boolean = false
    ): GLuint; //no return value unless CreateTextureObject is true

Page 1 of 2 12 LastLast

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •