Skip to content

stop using float[2], float[3]... in the public interface #788

@ghost

Description

I have a modified unvanquished codebase which almost got rid of the C Vector API, replaced by glm library, but I'm hitting daemon's limitations as cgame interacts directly a lot more with the engine than sgame.

I originally though it would be a good idea to just rely on exposing glm in the public interface, but thinking more about it, this might not be such a good idea, as in theory one might be willing to switch to a different lib someday (or even today, as glm as yet to go in daemon).

One of the major issues with the current API is that it defines arrays, which means (pointers to) arrays are passed all around. Those can be (and are!) randomly abused to pass optional values, without anything in the signature or "documentation" making it obvious that the array is meant to be an actual mandatory value (a reference, in C++), an optional value (*), or the start of an array of data...
Other issues are:

  • almost (to be optimistic) no documentation
  • macro based
  • requires out parameters, which implies declaring variables before calling the functions, preventing code like this: vec3_t angles = RotatePointAroundVector( vec3_t const& dir, vec3_t const& point, float degrees );
  • C-stuff, makes simple maths expressions hard to read: you can't do vec3_t destination = start + direction * distance;

All the stuff I'll discuss also apply to vec2_t, vec4_t, mat3_t, etc.

I intend to finish the work of getting rid of the Vector API in my gamelogic work very soon (in February, to be exact) but as said on IRC, I can see multiple ways of doing it, some would bring more benefits to everyone, others would be completely independent and hence would not benefit anyone but my code:

  • migrate daemon's public API to glm:

    • no compat layer for Unvanquished as it is moving toward glm
    • introduces a 3rd party dependency on the public interface, that I tend to consider a rather bad thing for long-term compatibility, but this would still be better than current situation, as glm at least is fully documented and maintained
    • this benefits everyone
    • probably bulky, will introduce lot of changes to review, which imply long discussions that will bore everyone, not to mention the code reviewing
    • breaks API compatibility but probably keeps ABI compatibility with gamelogic
  • implement an alternative public API using glm

    • no compat layer for Unvanquished as it is moving toward glm
    • introduces a dependency on the public interface
    • benefits everyone
    • can be merged in atomic chunks, easier to review
    • can be optional, driven by a compilation flag for example, to allow running both experimental and stable codebases
    • breaks API compatibility but probably keeps ABI compatibility with gamelogic
  • migrate daemon's public API to something like union vec3_t { float d[3]; struct { float x, y, z; } xyz; struct { float r, g, b; } rgb };

    • requires compat layer when gamelogic wants to use a 3rd party library, but C++ allows to easily implement cast operators (which is not true for the current typedef stuff)
    • no external dependency
    • this benefits everyone
    • breaks API compatibility but probably keeps ABI compatibility with gamelogic
    • can be implemented with a bunch of trivial, automated changes
    • suffers the lack of documentation and maintenance problems of current API
  • gamelogic can implement some hackish proxy that no longer include daemon's definition for structures like playerState_t, refdef_t, etc

    • benefits only gamelogic that does it
    • no need for review or talking as won't be merged
    • no API/ABI breaks
    • ugly and hackish

Those are the 4 solutions I see.
I'll rely on the hackish solution for myself if the discussion goes nowhere or the end decisions would require too much additional work from me: as I said, I intend to complete the glm migration of my codebase next month. To give you an idea about how many work I still have to do, grep can only find 98 occurrences of vec3_t in my codebase, so yeah, I'm quite serious about finishing this stuff soon.

*: in C, there was only 2 choices: use pointers, or implement a function with a proper name. C++ have two more options: default values (that I dislike because they make code more confusing), and using a different signature with the same function name.
In daemon or unvanquished, notable examples of better function name are the trace functions, which can be either about RayTracing (no box sizes) or about BoxTracing (with the dimensions of the box to swipe around the world). There are other occurrences, but this is out-of-topic.

Imo where pointers can be useful in C++, but C++ also have default mechanisms - that I personally dislike - and even C allows to just define a function with a name describing the

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions