Progress toward C++0x capability compliance

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

Progress toward C++0x capability compliance

Michael Lucas-Smith-2
As you may have read, C++0x is going to a final vote in March, which means it is almost a reality as the new C++ standard. Since we are all so close to having another fantastic specification to follow, I thought it wise to review our progress in Smalltalk 7.8 toward compliance with this specification as it stands now. http://www2.research.att.com/~bs/C++0xFAQ.html

I apologize for the length of this update, but it has been 12 years since C++ last put out a standard, so we haven't had anything to measure up against in a while.

You may be aware that "concepts and axioms" as a feature was dropped from C++0x and so no work has been done on implementing, although an implementation of "traits" has been available across many Smalltalkers for a number of years, this is not the same kind of trait as described for C++.

If you feel our support for any of the particular new features is lacking in some way that impacts your day to day use of Smalltalk, please reply to this thread with your suggestions. Note that all suggestions will be forwarded on to the C++0x standard committee before their final vote on the specification in March.



auto - deduction of a type from an initialize
        Unsupported, the deduction of a type is not something we provide in the standard base, however you are able to access the type and super types of an object by sending anObject class or if you want to avoid falling in to a proxy hole, in VisualWorks you can use Trippy.Inspector classOfObject: anObject.
        This feature is intended to reduce the amount of typing required to specify a type safe program. By including the type in the header of each object, we avoid the necessity of specifying the types to begin with, essentially obsoleting this feature. I'm sure you'll forgive us if we don't implement 'auto'.


Range for statement
        This reduces the amount of writing required to loop over ranges or objects with a defined begin and end. For example, instead of writing for(auto i = variable.begin(); i != variable.end(); i++) you can write for(auto i : variable). While we do not have the same syntax, we do have the same behavior with the #do: method. Like Range-For, #do: is implemented generically for any kind of collection, including strings.


Right-Angle brackets
        In previous versions of C++, the final right angle bracket of a template instantiation was confused with the selector >>, eg: std::vector<Vector<int, 3>> would be incorrectly parsed. Many C++ compilers already knew how to handle this, and so would display an error indicating what the problem was. All compilers are now expected to not mistake the template instantiation with the >> selector.
        When type-declarations are turned on, I can confirmed that a double >> in the comment of a class does not become mistakenly interpreted as a >> selector.


Defaulted and Deleted functions
        The common idiom of "prohibiting copying", eg: by implementing #copy as self shouldNotImplement; can now be expressed directly in C++ using = delete and its opposite = default (which is the default anyway and doesn't need to be specified). Attempts to call these methods at compile time will raise an exception.
        We do not currently support this compile time checking in Smalltalk, but we do catch such exceptions at runtime and raise an exception that can be optionally handled downstream in your process.


Enum Class -- scoped and strongly typed enums
        Embarrassingly, we have to admit we never implemented non-scoped, non-strongly typed enums. We've only ever had correctly scoped enums in the form of class-side methods (or instance-side methods if your enum is dynamically constructed). The C++98 enum feature is only available on ExternalInterface subclasses, not as a general programming mechanism.


constexpr -- generalized and guaranteed constant expressions
        The API for constexpr in Smalltalk is slightly different to what the specification is suggesting we implement. anObject beImmutable is used to at runtime specify that an object is constant. Any method literals are also constants by default. We do not have guaranteed constness though, as you can always tell an object to #beMutable later. I consider this feature partially implemented however it seems unlikely we'll be adding guaranteed constness in the near future.


decltype -- the type of an expression
        This has been implemented a long time ago and re-uses the same API available for the new 'auto' feature. After evaluating an expression you can send #class to the resulting object to find out its type. This cannot be done introspectively though, as it can be done by the C++ compiler. However, as the specification states, decltype is not typically used like that.


Initializer lists
        Collections and Paired-Collections can now be initialized from a literal array of values, eg: Array withAll: #( 1 2 3 4 ). However, the more interesting case for Dictionary withAll: #(#(1 2) #(3 4) #(5 6)) is not currently supported.


Preventing narrowing
        It is a problem when a richer numeric type is truncated in C/C++ implicitly, eg: void f(int); f(7.4) will truncate the integer to 7. The numeric library in Smalltalk always upcasts/promotes objects in to a broader type when doing mathematical operations.


Delegating constructors
        In previous versions of C++ you had to repeat the logic in your constructors multiple times because one constructor could not call another. This has never been the case in Smalltalk-80 and we are pleased to say that this is still the case. This feature is fully implemented.


In-class member initializers
        While we have in-class member initializers for shared variables, we do not have them for class variables, class-instance variables or instance variables at this time. There have been suggestions to objectify the instance variable slots from strings in to a Variable type object, in which case we would be able to add shared variable like initializers there too. However, no strong plans have been put in to motion yet.


Inherited constructors
        In C++0x it is now possible to pull up from a superclass, initialization methods in to your inheriting class for overloaded constructors. Unfortunately, we do not do function overloading by arguments, only by receiver, so at this point in time we do not have the ability to provide this. You can however, alias the superclass initializer, eg:
                A class>>makeMe: anInteger
                        B class>>makeMe: aFloat
                        B class>>makeMeInteger: anInteger
                                super makeMe: anInteger
        This does not cover all inheritance situations. For more powerful aliasing techniques through the superclass hierarchy, look at Travis Grigg's "super super" implementation.


Static (compile-time) assertions -- static_assert
        No compiler support has been added for this, nor tool support. But at compile time it is possible for you to use a different #compilerClass for your class that can check assertions on method recompilation. Including indicators in to the Refactoring Browser should not be difficult. As of this stage, we have no plans to provide this capability.


Long long -- a longer integer
        Our LargeInteger classes are already as long as memory allocation can support, so luckily we have already implemented this. Support for the "long long" type in ExternalInterface may need to be reviewed however.


Nullptr -- a null pointer literal
        The "nil" keyword can be used to indicate an object that is "null"-like and behavior can even be added to this nullptr, which is a feature beyond the C++0x specification.


Suffix return type syntax
        Now that C++0x has closures, they have also added a new syntax that allows you to specify the return type of a closure within the scope of the closures namespaces. Instead of returntype (arguments) {code} you can now write [](arguments) {code} -> returntype. Since we don't specify the returntypes, this change is not something required by the Smalltalk language. We do note that the C++0x specifiers must have noticed our syntax for closures [] and given us a well deserved tip of the hat.


Template alias (formerly known as "template typedef")
        There are a number of techniques for creating aliases in Smalltalk. Some of them are well supported and known, but even classes and namespaces can be aliased inside a namespace. The most typical way to create an alias is to simple implement a method, such as:
        MyAllocator>>MyString
                ^String new


Variadic Templates
        As we don't implement templates yet, we cannot implement variadic templates. However, you can pass strongly typed non-manifested arrays of objects to a method and operator on them, which reduces the cost of typing out the algorithm considerably. We recommend this technique over the new C++0x variadic templates. Likewise, we recommend using regular Arrays or OrderedCollections over the new Tuple and make_tuple() features.


Uniform initialization syntax and semantics
        This is a new C++0x feature that will be a real time saver. One uniform way of defining a list that will instantiate arrays, objects and vectors of any kind. The Smalltalk base library has long suffered from numerous APIs for initializing objects, from the simple:
                Object new
        to the downright outrageous:
                SortedCollection sortBlock: [:a :b | a >= b]
        Alas, there is no plan at the moment to provide the uniform initialization syntax, however if we did it might look like this:
                Object {}
                SortedCollection {[:a :b | a >= b]}
        ...or not


Rvalue references
        C++ has never had the ability to modify the LHS of an expression by using the RHS value. There are numerous situations where this can be quite nasty, eg:
        true become: false
        But also situations where it can be quite useful. Oops, I've let the cat out of the bag. We support two kinds of Rvalue modification in Smalltalk, the first is called #become: which swaps the left and right values in a single operation; the second is oneWayBecome: which is C++0x's new Rvalue assignment construct.
        Some implementations of Smalltalk perform these become operations very quickly and some libraries will use them to provide a more optimal solution that object wrappers or proxies.


Unions (generalized)
        In C++98 (as in earlier versions of C++) a member of a union with a user-defined constructor, destructor or assignment could not be a member of a union. This has never affected Smalltalk, since we don't implement unions outside of ExternalInterface. We may need to update our union support there to remove the C++98 limitation.


PODs (generalized)
        A POD (Plain old data) is something that can be manipulated like a C struct (eg: instVarAt:, instVarAt:put:). In C++98 the actual definition of the POD is based on a set of restrictions on the use of language features. If your struct or class includes functions, it was not declared as a POD. In C++0x, this restriction is removed, however if your struct or class contains a virtual function, it will not be considered a POD. In Smalltalk, we consider all classes and primitives as PODs, which goes above and beyond the C++0x requirement.


Raw string literals
        Consider a regular expression such as '''''''''' .. to represent that in a Smalltalk string would require:
                ''''''''''''''''''     " I hope I got that right"
        Note that the quote character is represented as two quotes. A raw string literal allows you to define the escape character or sequence outside of the string. However, we have no plans at the moment to support this new syntax in Smalltalk.


User-defined literals
        Smalltalk provides several literals for a variety of built-in types:
                123 "int"
                1.2 "float (double in some smalltalks)"
                1.2d "double (fixed decimal in some smalltalks"
                $a "character"
                8903218903128903891023 "unsigned long long"
                16rD0 "hexadecimal unsigned"
                'as' "string"

        However, in C++98 there are no literals for user-defined types. This can be a bother and unfortunately, in Smalltalk, it'll remain that way. The proposed syntax in C++0x is to suffix strings with a selector, which agreeably works well for Smalltalk, allowing us to change nothing in the language and execute the new user-defined literals. They will not be literal evaluated at compile time though.
                z = 2 + 1i "returns a Complex(2, 1)"


Attributes
        Attributes is a new standard syntax aimed at providing some order in the mess of facility for adding optional and/or vendor specific information into source code. In Smalltalk, comments remain the only viable option for cross-smalltalk vendor specific information. That and using vendor specific frameworks and methods. Use those to your hearts content.


Lambdas
        While new to C++0x, Lambda has been available as a complete closure mechanism under the name of "Blocks" in Smalltalk for a very long time. It even has its own syntax of [:arguments | code]. The C++0x syntax is slightly different to the Smalltalk block syntax and surprisingly adds a new feature which we support implicitly.
        [] (arguments) {code}
        It is easy to agree that they have taken notice of our syntax but also wanted their new syntax to remain like the C family of languages with {} representing code. That is understandable. The interesting twist is the use of []; it contains a list of variables to close over.
        [] -- close over no variables
        [&] -- close over all variables as references
        [=] -- close over all variables as copies
        or a list using & and = to specify individual variables accessible within the outer scope.
        Smalltalk already automatically determines which variables are used inside the closure and closes over those for you. So while we admire the neat control mechanism they've come up with C++0x, we feel our solution is more elegant.


Thread-local storage (thread_local)
        Sorry, I have not had time to write this entry. Please come back later.


unicode characters
        VisualWorks is entirely Unicode from the VM up now.


copying and rethrowing exceptions
        How do you catch an exception and then rethrow it on another thread? Well exceptions are just objects, so you send it to the other thread and call #resignal or whatever applicable API works best in your implementation of Smalltalk


extern templates
        Unfortunately, since we do not implement templates, we cannot implement extern templates.


Inline namespaces
        Support for namespaces in namespaces with variable import declarations is not something universe to Smalltalk, but is available in VisualWorks and variants of it are available in many other Smalltalks.


explicit conversion operators
        C++98 provides implicit and explicit constructors. In Smalltalk, we only have explicit ones, so we never had the implicit conversion problem that has plagued C++ for its many years of horrible demeaning existence. Examples of explicit conversions at #asString and #asArray.


Algorithm improvements
        Many of the algorithms in C++0x now support the use of Lambdas. Our standard library has done this for a good while, so there's nothing new to report here.


Container improvements
        Now that the Rvalue can be passed back, several of the containers have been improved in C++0x to be "costless" in passing big immediate arrays back from a method. This was never a problem in Smalltalk because all objects are allocated on the heap and only handles are ever pass back. So, technically we can't implement this one, sorry.


Scoped Allocators
        While the allocator pattern has never really taken off in Smalltalk, beyond a few generic selectors on Object (new, new:,  basicNew, basicNew:) it is worth noting that in C++0x you can now have allocators that contain state. This was never a restriction in Smalltalk.


std::array
        A new standard container 'array' which is a fixed-sized random-access sequence of elements has been added in C++0x. We've had Array for a while. Keep up.


std::forward_list
        A new forward_list which i a linked list that can only be iterated forwards has been added. We do not currently have something that is equivalent in Smalltalk.


Unordered Containers
        Four new unordered container classes have been added to the STL. These containers are unordered in that they store their elements by using hashes. Similar classes already exist in Smalltalk and recently AndrĂ©s Valloud improved our stock standard hashing functions and also created a suite of tools for testing the efficiencies of new/different hashing functions.


metaprogramming and type traits
        Sorry. Come back later.


std::function and std::bind
        The bind and function standard function objects allow you to create "messages" that can be sent later. We have these facilities in Smalltalk under the name Message and MessageSend as well as the ability to use reflection to fetch a CompiledMethod that acts as the equivalent of std::function.


unique_ptr and shared_ptr
        Two new classes for handling the ownership of pointers and to automatically de-allocate them when appropriate. Completely irrelevant in Smalltalk as we have a garbage collector that saves you all the tedium of having to figure this out yourself. We call this feature "Moving on from the 70s" but that part of the proposal for C++0x was never considered a high priority. Hopefully it'll be a bigger hit sometime around 2016 with C++1x.


weak_ptr
        This allows you to point to an object/pointer and not have it be counted as an actual hard reference. This is useful for a garbage collector and apparently for people who feel the need to hurt themselves with unique_ptr and shared_ptr. We have it in two variants in VisualWorks, WeakArray and Ephemerons. Ephemerons are the more desirable because its name is very very cool.


Garbage collection ABI
        The C++0x standard provides an ABI for declaring which portions of memory are not going to be reachable and/or do not contain pointers that need to be traced. Our strongly typed system already knows that byte arrays will not contain pointers to objects and that everything else is pretty much going to be an object. So, we currently have no immediate use for the API. We may add it as a NO-OP for compatibility.


Memory Model
        Memory alignment. It's a good thing. We has it.


Threads
        C++0x now has support for threads. To create a thread, you instantiate an instance of std::thread passing in either a std::function or std::bind for the thread to run. In Smalltalk we use a closure instead of a bind/function, which allows you to optionally run a bind/function equivalent inside the closure. We believe our API has been thought through better by people who actually write programs for a living.

        There are however, several pieces of API that we currently do not support. The first is #join which makes your thread halt until the other thread has finished:
        thread1 := [self doStuff] fork.
        thread2 := [self doMoreStuff] fork.
        thread1 join
        thread2 join.

        Currently, to simulate this you need to create semaphores and wait on them yourself. The C++0x specification does not contain a way to terminate a thread. In fact, there was great opposition to having an API for this purpose. Despite this, we have an API for this which we call #terminate. The thread itself can intercept this because it is inserted as an exception in to the running process, which gives both the parent and child thread a way to negotiate the terms of its surrender.


Mutual Exclusion
        C++0x now includes a new class mutex and recursive_mutex. While we don't have a class specifically for them, we have Semaphore instead which can be used as a mutex or a conditional variable, as you see fit and a recursive variant called RecursionLock which only acts as a mutex lock.
        For the most part, our behavior is equivalent, except that we do not have the lock* classes which automatically release a lock when it goes out of scope. The Semaphores in Smalltalk already unlock when they go out of scope, so this is not a loss for Smalltalk, but rather a win in reduced complexity.
        Some API we currently don't support though are:
        try_lock / try_lock_until -- testing whether a mutex is already locked and if it is not, locking it as a single atomic operation.
        Mass mutex operations:
                lock(multiple mutexes)
                try_lock(multiple mutexes)
                unlock(multiple mutexes)


Time utilities
        We often want to time things or do things dependent on timing. Smalltalk has a rich set of time capabilities that are on par with C++0x. The C++0x supports a large range of standard unit prefixes from yocto up to yotta. We don't quite go that far, but we do have the same support for declaring a Duration with a class we call Duration. The class in C++0x <chrono> is also called std::duration and they both support much the same maths API.


std::future and std::promise
        We don't have the same API for future promises, but we do support them already with a closure API called #promise. Unlike the C++0x promises, the promise is automatically shared between parent and child threads. A promise will allow you to wait in the parent thread for a value while the child executes, or optionally not ask for the value until later. In C++0x the API to get the value while blocking is get() while in Smalltalk we have it implemented as #value.
        You can also send wait_for(aDuration) to see if a value has become available in the promise yet. In Smalltalk we have the #hasValue API which is immediate without a timeout, allowing you to compose a failover condition however you desire.


std::async
        C++0x has wrapped up the standard "do stuff in another thread and return a result" behavior in a convenient but limited method called std::async. We do not currently support anything simpler than:
        [self doStuff] promise value.


Random number generation
        Random numbers are useful in many contexts, such as testing, games, simulation and security. C++0x now supports a standard API for random numbers and random number generators. We too have a implement Random as a stream of random numbers. We have multiple implementations at your disposal in the standard library.


Regular Expressions
        While not part of the core language as it now is in C++0x, we do have an add-on library that is frequently being used by many commonly used high level frameworks. It might be possible to promote this library if there is enough interest.



(* disclaimer: VisualWorks does not support interfacing with C++. I also actually like C++, but that's not an invitation to sue me. Also note: none of your suggestions will be forwarded on to the C++0x standard committee before their vote in march, or ever.)
_______________________________________________
vwnc mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/vwnc