In Rust, generic code is fully validated as it should be, with all trait bounds being matched where need be. So you would rarely end up with needing to figure (at compile time) whether generic type T is an enum or not - because it wouldn't allow you to do anything extra with it out of the box. In Rust, an idiomatic way of adding different behaviours based on the kind of the generic type would be using traits still - e.g., you can have multiple impl blocks on your generic type, each one having different trait bounds on the generic parameter, like - if T: Copy, then implement these extra methods; standard library has tons of examples like this. The only exception that comes to mind is proc macros - when you deal with Rust code in AST form and generate new code at pre-compile-time phase, but that's a completely different story and not really related.
Indeed Rust proc macros, or Rust macros in general, are a better analog to C++ templates than Rust generics. They aren't quite the same, as during evaluation time C++ can do a bit more, and apparently type_traits is an example of what C++ can do statically. Rust's model is different, it wants macro expansion to be done by the time it starts even resolving any non-macro paths, let alone type checking or anything.
That being said, you'll be able to emulate something like that once specialization lands (hopefully also included by min_specialization).
Mind you it probably has very little real world usage right now..
Concepts aren't as powerful as Rust traits in that they are static dispatch only, while Rust supports both static & dynamic-- but they should make for cleaner code than <type_traits>
"Object-oriented programming: Some history, and challenges for the next fifty years"
https://www.sciencedirect.com/science/article/pii/S089054011...
Than read on BETA design for example,
Follow up with "The Art of the Metaobject Protocol",
https://mitpress.mit.edu/books/art-metaobject-protocol
and "Xerox LOOPS"
http://www.softwarepreservation.org/projects/LISP/interlisp_...
Then "Component Software: Beyond Object-Oriented Programming" (the 1st edition with Component Pascal)
https://www.amazon.com/Component-Software-Beyond-Object-Orie...
"Applying Traits to the Smalltalk Collection Classes"
https://rmod.inria.fr/archives/papers/Blac03a-OOSPLA03-Trait...
"Self – The Power of Simplicity"
http://media.cichon.com/talks/Introduction_Self_Language_OOP...
https://blog.rfox.eu/en/Programming/Series_about_Self/index....
How about this for starters?
https://news.ycombinator.com/item?id=11940050
On Understanding Data Abstraction Revisited
https://www.cs.utexas.edu/~wcook/Drafts/2009/essay.pdf
The Power of Interoperability: why objects are inevitable
https://www.cs.cmu.edu/~aldrich/papers/objects-essay.pdf
A closure is a poor man's object
http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/m...
This isn't knocking the book, I've read it and agree that it's edifying. This list is a good resource on OOP in general. It's just not helpful in the context of this thread.
Your own words, documentation and knowledge was shared, apparently you are the one not interested.
Again: Does one really, concretely, have to read The Art of the Metaobject Protocol, a book about Lisp, to evaluate this claim about C++? Having read the book, I would say no. Which is why I say that you are evading a real discussion.
(And for that matter, it would be interesting if you fleshed out your counter-claim -- "Other than the classes used to implement all those STL concepts." -- in a bit more detail. Are you saying that all uses of C++ classes are OOP? Or that only certain uses are, but that the way classes are used to implement the STL fall into this category? Will I find the answer to these concrete questions in The Art of the Metaobject Protocol?)
Definitely, any use of class constructs with data members and member functions is OOP.
Any use of data members inside class constructs represents aggregation and if any member function on a data member happens to be called, as means to help a member function to do their work, that is delegation.
Any template implemented as class that uses its type parameters to decide at compile time what gets called, makes use of dynamic dispatch decided at compile time.
If you prefer we can pick any random STL type and dissect all their uses of OOP features, lets start with something like std::set?
Definitely not.
OOP requires, also, runtime binding and polymorphism. You can get the polymorphism by inheritance, delegation, or what-have-you. You can get runtime binding with a vtable or a name lookup.
The usual term for just-encapsulation is "object-based". The STL is definitively not object-oriented, according to its author, Stepanov, who has said that giving the STL classes member functions was a mistake.
OOP is a niche technique. It has uses, sometimes. More often, a function pointer suffices.
Object based languages are part of the OOP universe from CS point of view, plenty of literature, that apparently is too much to ask to read about, some of which I have provided above.
So here we go about std::set and it being OOP.
1 - class set, a means for encapsulation, with most of its members de
2 - Uses delegation for memory allocation via the allocator_type
3 - Uses delegation for key lookups and value comparisasion
4 - Implements the concepts Container, AllocatorAwareContainer, AssociativeContainer, ReversibleContainer alongside their respective concept dependencies, which in OOP speak are protocols/traits/categories;
5 - The actual types used for comparisaion and allocation are instances of the respective protocols, with dispatch being decided at compilation time of std::set uses, given the respective implementations as type parameters, in a way similar to multi-method-dispatch.
As bonus here is a simplified UML diagram. It isn't more detailed, because my patience to draw ASCII art is limited.
-------------------------------------------------------------------------------------------
|std| |
----- |
| __________________ |
| | <<protocol>> | |
| | Container | |
| |-----------------| |
| |-----------------| |
| |-----------------| |
| | constructor | |
| | copy-constructor| |
| | destructor | |
| | begin() | |
| | end() | |
| | cbegin() | |
| | cend() | |
| | swap() | |
| | size() | |
| | max_size() | |
| | empty() | |
| ------------------- |
| /\ |
| -- |
| | extends |
| | |
| ______________ _______________________ _______________________ |
| | <<protocol>>| | <<protocol>> | | <<protocol>> | |
| | Allocator | | ReversibleContainer | | AssociativeContainer | |
| |-------------| |----------------------| |----------------------| |
| |-------------| |----------------------| |----------------------| |
| | constructor | | rbegin() | | key_comp() | |
| | destructor | | rend() | | value_comp() | |
| | allocate | | crbegin() | |----------------------| |
| | deallocate | | crend() | /\ |
| | max_size | |----------------------- -- |
| | construct | /\ | |
| | destroy | -- implements | |
| --------------- | | |
| /\ | | |
| -- | implements / |
| | \ / |
| | implements \ / |
| | \ / |
| | \------------------ |
| | | |
| | | |
| | ------------------ |
| ---------------- | <<protocol>> | |
| | allocator<T> | uses | set | |
| |---------------|<------------------------/\|------------------| |
| |---------------| \/| constructor | |
| | destructor | |
| | begin() | |
| | end() | |
| | cbegin() | |
| | cend() | |
| | rbegin() | |
| | rend() | |
| | crbegin() | |
| | crend() | |
| | operator= | |
| | get_allocator() | |
| | empty() | |
| | size() | |
| | max_size() | |
| | clear() | |
| | insert() | |
| | emplace() | |
| | emplace_hint() | |
| | erase() | |
| | swap() | |
| | extract() | |
| | merge() | |
| | count() | |
| | find() | |
| | contains() | |
| | equal_range() | |
| | lower_bound() | |
| | upper_bound() | |
| | key_comp() | |
| | value_comp() | |
| ------------------ |
|-----------------------------------------------------------------------------------------|Concretely, you cannot take a Container pointer and point it at a std::set. You can take a T, and bind T to std::set at compile time; but that is Generic Programming: analogous, in some ways, but not the same.
In the '90s it was fightin' words to say "X is not Object Oriented", because Object-Oriented was taken as a high-status way to say Good, and "Not Object-Oriented" translated implicitly to "Not Good".
But in our brave new world, object-oriented is but one design discipline, and we have others, and enough language primitives to construct our own disciplines by mix-and-match as problems dictate.
And, I have myself simplified code that had used a virtual function to use, instead, a function pointer, and it was (still) Good. Better, even.
What renowned SIGPLAN or IEEE paper states that runtime binding is required for 100% of all OOP programming languages ever created and polymorphic dispatch at compile time doesn't count?
Changing the definition would be moving the goalposts. A new definition deserves a new word.
I mean... in the sense in which most people use "OO language" (which is, "a language with constructs which associates a data specification with procedures"), yes. Only hardcore pure-functional algorithms written in Haskell / ML without typeclasses, or things like Prolog, Esterel (though thinking about it), and similar research languages aren't.
Once upon a time programming looked like this. https://github.com/chrislgarry/Apollo-11/blob/master/Luminar...
Plenty of SIGPLAN papers to one educate themselves about what OOP is all about.
OOP is not MFC alone, apparently those that learn languages on the trenches without referring to them, never get to appreciate the wealth of information we have available.
Not surprising, given how many misunderstand "whatever my compiler does" for what ISO actually says.
Back to STL, it makes heavy use at compile time method dispatch via template metaprogramming and lambdas (which are implemented as classes actually by the compiler.
I know this is an argumentum ab auctoritate, but you didn't really provide an argument either :)
> Yes. STL is not object oriented. I think that object orientedness is almost as much of a hoax as Artificial Intelligence. I have yet to see an interesting piece of code that comes from these OO people. In a sense, I am unfair to AI: I learned a lot of stuff from the MIT AI Lab crowd, they have done some really fundamental work: Bill Gosper's Hakmem is one of the best things for a programmer to read. AI might not have had a serious foundation, but it produced Gosper and Stallman (Emacs), Moses (Macsyma) and Sussman (Scheme, together with Guy Steele). I find OOP technically unsound. It attempts to decompose the world in terms of interfaces that vary on a single type. To deal with the real problems you need multisorted algebras - families of interfaces that span multiple types. I find OOP philosophically unsound. It claims that everything is an object. Even if it is true it is not very interesting - saying that everything is an object is saying nothing at all. I find OOP methodologically wrong. It starts with classes. It is as if mathematicians would start with axioms. You do not start with axioms - you start with proofs. Only when you have found a bunch of related proofs, can you come up with axioms. You end with axioms. The same thing is true in programming: you have to start with interesting algorithms. Only when you understand them well, can you come up with an interface that will let them work.
I have seen this a few times and have felt that "hoax" is the wrong word each time. Later he talks about OOP being "philosophically unsound", which adds some clarity to the statement. It's not that OOP or AI are made up (true hoaxes), it's that they will not accomplish (in his view) what they set out to do because they are founded on unsound bases.