The point is not a lack of objects but a lack of OOP. Yes, there are "objects" but there is also a separation between objects and methods etc..
The issue with m->set_volume(.08) is it becomes difficult to tell what happens inside set_volume() because in the method scope you have access to the internals of the "m" object, not just the interface.
With mixer_set_volume(m, .08) you have ".08" and "m" but only the interface "m" exposes anywhere else. Thus it becomes easier to reason about effects.
Can you expand on this? I'm not sure I follow, but maybe there's something particular to C++ here that I don't get as I'm not well-versed in it.
>> The issue with m->set_volume(.08) is it becomes difficult to tell what happens inside set_volume()
Isn't that the point? That you shouldn't have to care what happens inside set_volume? If mixer was designed and implemented well, then set_volume (along with the rest of its interface methods) should consistently and correctly handle its internal state.
Whether you are dealing with OOP, functional, or some other pattern it is always the point to create abstractions which you and others can forget about how they work.
Thus m->set_volume(.08) and set_volume(m, .08) should not require you to understand what happens internally as the user of either.
More generally in programming that is the point.
My only point is that of scope and essentially search space -- set_volume(m, .08) can only access the public interface of "m". While, m->set_volume(.08) could do almost anything to m. In a perfect world, with perfect programmers thinking the same way about what "set_volume" means or should do the separation of state and action would be less valuable.
This not to say OOP is > Functional or vice-versa. I think there is a place for both but that is a MUCH larger and more nuanced conversation :)
"Object" is such a generic term that one can call anything an object. However, in the OOP world, an entity is an object if it has been defined or created like an object in the language's syntax. So the answer to your question is: we don't know, show us its declaration.
But the truth is that one can write many different stories on this line. One can tell the OO story in which m is an object, one can tell the FP story in which m is an IOMonad, one can tell the procedural story in which m is a record.
All of these stories are more or less equally good if it's a single line of code. But on a whole program, some of them can be more or less believable at certain points. For instance, when the OOP story tells us that integers are objects and that "+" is a message to which integers answer by given the result of an addition, this part of the story can seem a little far-fetched for some people.
Is there such a thing as the "best story in the world"? Definitely not. There's not even a "my favourite story" for most people, but instead a "my favourite love story" and a "my favourite horror story", and so on. How does one come up with a favourite story? By reading stories.
> when people are talking about OO objects they mean data+behaviour
The distinction between behavior being "a set of methods defined inside a class", and "a set of methods whose first param is a particular type of struct" is not that important.
The distinction between "a set of methods with privileged access to encapsulated internal state of a class" and "a set of functions whose first param is a particular type of struct, and operate on members of that struct that everyone can access" is pretty critical though.
Definitely, and encapsulation is one of the good things you get out of well designed OO. But I don't think it's that relevant in regards to objects as data+behavior, and while it leads to much better designs, I don't think it's required—hence all of the C libraries written in a very OO style. So: yes, but not really relevant to the point being discussed.
The issue with m->set_volume(.08) is it becomes difficult to tell what happens inside set_volume() because in the method scope you have access to the internals of the "m" object, not just the interface.
With mixer_set_volume(m, .08) you have ".08" and "m" but only the interface "m" exposes anywhere else. Thus it becomes easier to reason about effects.