Overview
It is often asserted that all general purpose programming
languages can solve the same set of problems. This means that no one
programming language has functional advantages over any other programming
language.
That assertion is only mostly true, which means it is false.
For example, weakly typed languages can perform and make use
of implicit type conversions while strongly typed languages cannot. Strongly
typed languages must employ explicit conversions to achieve a similar effect.
The purpose of this article is to begin discussing some of
the things that are difficult in one commonly used language and relatively easy
in another.
Scalar Ranges
Programming languages derived from Pascal syntax allow
scalar types and subtypes to be defined by the programmer, while programming
languages derived from C syntax do not allow the programmer to define scalar
types or subtypes.
In C++, for example, a class must be declared encapsulating
the behavior of a scalar type with a programmer specified range of values.
Making a subtype of that class then requires the creation of an inherited class
expressing the restrictions distinguishing the subtype.
In C++ enums are encapsulated in a class as illustrated by
the following Stack Overflow issue:
How can I implicitly
convert an enum to its subset and vice versa in C++?
More precisely, the feature I want is like implicitly convert an enum to its subset enum and vice versa.
The code I wish it working:
enum class Human { A = 1, B = 2, }; enum class Male { // subset of Human A = Human::A, }; enum class Female { // subset of Human B = Human::B, }; // some functions can handle
all humans void human_func(Human h) { // ... } // some only take a subset of
humans void male_func(Male m) { // ... } void female_func(Female m) { // ... } // and user only uses values
of Human as token constexpr auto SOMEONE =
Human::A; int main() { human_func(SOMEONE); // ok male_func(SOMEONE); // also ok, Human::A implicitly converted
to Male female_func(SOMEONE); // failed, can't
convert Human::A to Female. } |
But enum cannot do the
conversion. Now I have two options:
// 1. static_assert with template parameter
template void female_func()
// 2. manually convert it
#define _ENUM_TO_ENUM(e1, e2) \ static_cast<e2>(static_cast<std::underlying_type_t<decltype(e1)>>(e1))
void female_func(_ENUM_TO_ENUM(SOMEONE, Female))
|
As is shown above, the concept of a scalar range and
subrange is complicated by the need in C++ to express such a type as a class.
One answer provided to this question is
enum class
struct
struct
struct
void human_func(const Human & h)
void man_func(const Man & m)
void woman_func(const Woman & w)
|
It is clear that this approach may work for an enum with 2
values, but becomes unusable with an enum containing tens or hundreds of
values.
The Ada programming language, on the other hand, uses the
concept of scalar ranges and subtypes extensively.
The Character type in Ada is an enumeration type with the
range of values expressed as nul .. 'ΓΏ'. The ASCII characters are a subset of
the Character type with value in the range of nul .. del. Within the ASCII
characters the upper case characters are the range ‘A’ .. ‘Z’ and the lower
characters are the range ‘a’ .. ‘z’.
If the programmer wants to pass only upper case characters
as a parameter to a procedure the procedure can be defined as
subtype Upper
is range (‘A’ .. ‘Z’); procedure Upper_Action(U
: Upper); |
This procedure will only accept characters in the range specified by the subtype Upper.
A function that counts all the upper case characters in a string
can be defined as
function Count_Uppers
(S : in String) return Natural is Count : Natural := 0; begin for value of S loop if S in Upper then Count := Count + 1; end
if; return Count; end Count_Uppers; |
The Ada samples above exhibit the behavior and usage
requested by the person using C++ in the Stack Overflow question above.
The Ada program is not encumbered with the heavy syntax and
rules associated with C++ classes.