8 ms·
> Having to pass the object explicitly every time feels clunky, especially compared to C++ where this is implicit. I personally don't like implicit this. You
by 1718627440 1y ago
> Having to pass the object explicitly every time feels clunky, especially compared to C++ where this is implicit.
I personally don't like implicit this. You are very much passing a this instance around, as opposed to a class method. Also explicit this eliminates the problem, that you don't know if the variable is an instance variable or a global/from somewhere else.
- spacechild1 1y ago> Also explicit this eliminates the problem, that you don't know if the variable is an instance variable or a global/from somewhere else. People typically use some kind of naming convention for their member variables, e.g. mFoo, m_Foo, m_foo, foo_, etc., so that's not an issue. I find `foo_` much more concise than `this->foo`. Also note that you can use explicity this in C++ if you really want to.
- 1718627440 1y agoIn code I write, I can know what variables mean. The feature loses its point, when it's not mandatory. Also being explicit allows you to be more expressive with variable name and ordering.
- elteto 1y ago...and C++ added explicit this parameters (deducing this) in C++23.
- loeg 1y agoI think the author is talking about this: object->ops->start(object) Where not only is it explicit, but you need to specify the object twice (once to resolve the Vtable, and a second time to pass the object to the stateless C method implementation).
- 1718627440 1y agoYes I know. From the caller that might seem to be redundant, my argument was about the callee's side. Also it is not truely redundant, as you can write: object1->op->start(object2) superclass->op->start(object)
- loeg 1y agoI think both of these invocations are invalid. Using object1's vtable methods on object2, obviously, but in the latter case: the vtable method should just point at the superclass impl, if not overridden. And if overridden and the child impl needs to call the superclass, it can just do so without dispatching through some vtable.
- 1718627440 1y agoWhen you think of vtables as unique or owned by an object, then these example seem weird to you. When you think of them as orthogonal to your types/objects, these examples can be useful. In the first example, object1 and object2 can very much be of the same type or compatible types/subtypes/supertypes. Having vtables per object as opposed to per class to me indicates, that it IS intended to modify the behaviour of an object by changing it's vtable. Using the behaviour of another object of the same type to treat the second object, seams valid to me. In the second case, it's not about the child implementation dispatching to the superclass, it's about some external code wanting to treat it as an object of the supertype. It's what in other languages needs an upcast. And the supertype might also have dynamic behaviour, otherwise you of course wouldn't use a vtable.
- loeg 1y agoI think it is wrong/weird for objects of the same type to have different vtables, yes. I would call those different types. Upcasting is fine, but generally speaking the expected behavior of invoking a superclass method on an object that is actually a subclass is that the subclass method implementation is used (in C++, this would be a virtual/override type method, as opposed to a static method). Invoking a superclass-specific method impl on a subclass object is kind of weird.
- 1718627440 1y agoIn most languages, this is not possible, because they abstract over the implementation of classes. In C it is so you can be more creative. You can for example use it instead of a flag for behaviour. Why branch on a variable and then call separate methods, when you can simply assign the wanted implementation directly. If you want to know, which implementation/mode is used, comparing function pointers and scalar variables amounts to the same. It is also an easy way to get a unique number. When all implementations of that can operate on the same type, they are interchangeable. In C you can also change the "class" of an instance as needed, without special syntax. Maybe you need to already call a method of the new/old class, before/after actually changing the class type. > is that the subclass method implementation is used The entire point of invoking the superclass method is, because the subclass has a different implementation and you want to use the superclass implementation.
- ginko 1y agoNothing a little macro magic couldn't fix.. #define CALL(object, function, ...) (object->ops->function(object, __VA_ARGS__))
- ActorNightly 1y agoYou can also just replicate the vtable of sorts in C that keeps track of things when new objects are created.
- loeg 1y agoYou can just use C++ instead of reinventing a worse version of C++98!
- ryao 1y agoIt would be an improvement on C++ since you would avoid the horrendous error messages from ridiculously long types, slow compile times, exception handling nightmares, bloat from RTTI and constant worry that operators might not mean what you expect. That is before even mentioning entirely new classes of problems like the diamond problem. Less is more. That said, similar macro magic is used in C generic data structures and it works very well.
- ryao 1y agoWhat guarantee do you have that ->ops is a vtable? It could contain function pointers that don’t take an implicit this argument like struct file_operations in Linux does. It could also contain variables that are non-pointers. Neither is allowed in a vtable, but both are fine to do in C.
- 1718627440 1y agoAs it isn't the compiler that creates the vtable, you can also have the equivalent of this as the last parameter or where you want it to be. > It could also contain variables that are non-pointers. The convention of it being a pure vtable is that it just doesn't. > Neither is allowed in a vtable Who is the vtable membership authority? :-)
- ryao 1y agoYou can take the API for a linked list and implement a balanced binary search tree behind it, but continuing to call it a linked list after doing that is wrong. Similarly, you can do pointer indirections the same way a vtable would do them, but if the things you get are not the equivalent of member function pointers, it is not a vtable.
- 1718627440 1y ago> In computer programming, a virtual method table (VMT), virtual function table, virtual call table, dispatch table, vtable, or vftable is a mechanism used in a programming language to support dynamic dispatch (or run-time method binding). (Wikipedia) When its a table of function pointers used for dynamic dispatch, to me it's a vtable. I don't care about their type signatures as long as they logically belong to the object in question. You seem to have a different very narrow definition of vtables, so the discussion is kind of useless.
- ryao 1y agoRead the definition again. The programming language here is not the one using this. It is the programmer using it.
- ryao 1y ago“this” is a reserved keyword in C++, so you do not need to worry about it being a global variable. That said, I like having a this pointer explicitly passed as it is in C with ADTs. The functions that do not need a this pointer never accidentally have it passed from the developer forgetting to mark the function static or not wanting to rewrite all of the function accesses to use the :: operator.
- wmanley 1y agoIt’s not about ‘this’ being a global, it’s if you see ‘i++’ in code it’s not obvious if ‘i’ is a member or not without having to check context.
- Kranar 1y agoIf you see "i++" in code and you don't have any context about what "i" is, then what difference does it make if "i" is a member variable, global variable, parameter, etc etc... If all you see in code is a very tiny 3 character expression, you won't be able to make much of a judgement about it to begin with.
- ryao 1y agoNot allowing a variable to implicitly refer to a member variable makes it much easier to find. If it is not declared in the function and there is no implicit dereferencing of a this pointer, the variable is global. If the variable name is commonly used and it is a member variable, it is a nightmare to hunt for the correct declaration in the codebase with cscope.
- ryao 1y agoGood point. I had misunderstood the previous comment as suggesting that this be passed to the member function as an explicit argument, rather than requiring dereferences of this be explicit. The latter makes far more sense and I agree it makes reasoning about things much easier.
- ActorNightly 1y agoYou can also get clever with macros.
- MontyCarloHall 1y agoAgreed, one of the biggest design mistakes in the OOP syntax of C++ (and Java, for that matter) was not making `this` mandatory when referring to instance members.
- chuckadams 1y agoC++ and Java went for the "objects as static closures" route, where it doesn't make any sense to have a `this`. Or, they made them superficially look like static closures, which in hindsight was probably not the best idea. Anyway, Java lets you use explicit `this`, I don't recall whether C++ makes it into a footgun or not.
- MontyCarloHall 1y agoBoth languages let you use explicit `this` but don’t mandate it. The “static closure” approach is great. I don’t like having to explicitly pass `this` as a parameter to every method call as in the OP (or worse, the confusing Python approach of forcing `self` to be explicitly written in every non-static method signature but having it be implicitly passed during method calls). What I don’t like is being able to reference instance members without `this`, e.g. void foo() { int x = bar + 1; // should be illegal: it can be hard to distinguish if `bar` is a local variable versus an instance member int y = this->bar + 1; // disambiguation is good }
- josefx 1y ago> int x = bar + 1; // should be illegal: it can be hard to distinguish if `bar` is a local variable versus an instance member If it was this->bar it could be a member, it could also be a static variable. A bar on its own could be local or it could be in any of the enclosing scopes or namespaces. Forcing "this" to be explicit doesn't make the code any clearer on its own.
- ryao 1y agoThe guy was referring to the explicit case where bar is a member variable. The cases where it is in the local scope under scoping rules or the global scope are not really an issue, since you can check the function definition to find if it is local. In the case that it is not in the function definition, then it is in the global scope in C. If implicit this were not done in C++, that would also be the case in C++, provided you do the sane thing and use the std namespace for everything. Just thinking about the headaches namespaces could cause when looking for such definitions with cscope gives me yet another reason to stay away from C++ whenever possible.
- Gibbon1 1y agoThe implicit this sounds to me like magic. Magic! Ask how do I do this, well see it's magic. It just happens. Something went wrong? That's also magic. After 40 years I hate magic.
- Galanwe 1y agoI don't quite agree, especially because the implicit this not only saves you from explicitly typing it, but also because by having actual methods you don't need to add the struct suffix to every function. mystruct_dosmth(s); mystruct_dosmthelse(s); vs s->dosmth(); s->dosmthelse();
- 1718627440 1y agoMy problem with implicit this is more, that you can access member variables, without it being explicit, i.e. about the callee, not about the caller. For the function naming, nothing stops you from doing the same in C: static dosmth (struct * s); s->dosmth = dosmth; That doesn't stop you from mentioning s twice. While it is redundant in the common case, it isn't in every case like I wrote elsewhere. Also this is easily fixable as written several times here, by a macro, or by using the type directly.
- Galanwe 1y agoThis is not the same, you introduced dynamic function resolution (i.e.a function pointer tied to a specific instance), we are talking about static function resolution (purely based on the declared type).
- 1718627440 1y agoTrue, if you don't trust the compiler to optimize that, then you must live with the C naming.