What to think about when using pointers and how they effect API design?

So I'm currently designing and implementing my animation system for my game and I've come across some thoughts that I wanted to get some advice on. So far I've completed my first pass of the animation system api using a compression oriented approach to basically get things working. I don't have all the aspects worked out but enough so that I want to tighten up the API a bit before moving on. After mocking out an easily readable API my class's would now look something like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
struct Skeleton
{
   //some other members
   Dynamic_Array<Bone> bones;
}

struct Animation
{
   //some other members
   Dynamic_Array<Bone*> bones;  
}


The animation struct would have bone pointers which point to the Skeleton's bones. With this current setup I have function which setups the skeleton for the given animation and that would look something like this:

1
2
3
4
5
6
7
8
9
void SetupPose(Anim* anim)
{
   //Other code

   for(i32 boneIndex{}; boneIndex < anim.bones->size; ++boneIndex)
   {
      anim.bones->at(boneIndex).rotation = newRotation;
   }
}


After observing this function I didn't like the fact that passing in an animation will actually effect the skeleton's bone positions without it being immediately apparent to the caller. With this setup I could forsee many functions that could be effecting a skeleton's bone properties without ever having to pass in the actual skeleton to the function, again possibly making things ambiguous as to where my skeleton gets modified in the code base.

After some more thought though I realize that this kind of thing is probably happening in a lot of places around my code base, given my sort of cavalier attitude towards pointers. For whatever reason this particular instance caught my attention when other situations didn't.

My questions are is this sort of situation something I should be trying to avoid or not really a big deal? Is this what people talk about when suggesting to avoid pointers whenever possible as they can make things less understandable? How should one generally go about managing pointers when making API decisions?


Edited by Jason on
I don't think the issue is about pointer.

If you want the API to make it clear that it's modifying (or at least using) the skeleton, you can make it so that the user needs to pass the skeleton to the function (pointer, reference, id ...).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
struct Skeleton
{
   //some other members
   Dynamic_Array<Bone> bones;
};

struct Animation
{
   //some other members
   /* No array here. */
};

void SetupPose(Anim* anim, Skeleton* skeleton)
{
    ...
}


Whether or not it's a good idea to make it more visible to the user that SetupPose will modify the skeleton is up to you. I personally prefer to be explicit about the data a function will use by passing the data as arguments. But I sometimes bundle together in a struct "utilities" that are not the direct subject for the function. For example bundling the different renderer batches state (text, rectangles, images...) into a render context to avoid passing those to each functions that can render something.
I personally prefer to be explicit about the data a function will use by passing the data as arguments.


Ya, this is what I generally like to do as well, which is maybe why this particular situation made itself known to me.

I don't think the issue is about pointer.


Ya, I think this is more of an API design issue and just having some sort of guidelines when designing an API. I have a good first pass which is the compression oriented approach and just doing the simplest thing that works but when I'm trying to refactor something into a more usable state I get bogged down in trying to decide the best route forward. Right now I just basically mock out something I think I would like to use and then while actually implementing it I hope my "code smell" radar will go off and help me make the correct design decision. I guess what I really want to know is if there are some high level guidelines that people use when trying to design an API that helps them make better decisions, something sort of akin to the SOLID principles of object oriented programming
boagz57
...but when I'm trying to refactor something into a more usable state...

Is it really necessary to refactor ? If so why ? The "why" is probably part of the answer you're looking for.

After your "compression oriented" pass you should have enough information to "Write the usage code first" for a new version of the API.

Another "guide line" would be to "make the simple things easy AND at least make the hard things possible".

Casey's Designing and Evaluating Reusable Components - 2004 might have some answers.