-
Notifications
You must be signed in to change notification settings - Fork 264
Description
1. Preface
Literal suffixes are syntactic sugars to constructors. Considering Unified Function Call Syntax for Member Functions and Non-member Functions, this suggestion is somehow something similar for Literal Suffixes and Constructors.
Briefly, I suggest to support this:
a: = 1'000ul * 5.0float;
b: = "text"s8.size();
c: = (2.5litre)water.weight();
d: = ((1my::box)apple + (2my::box)orange).weight();
name: = ask_player_name();
age: = ask_player_age();
p: = (name, age)my::player;
p.buy(()m4gun, 30bullet);
I have to explain:
ul
ands8
are type aliases in1'000ul
and"text"s8.size()
respectively.(2.5litre)water
creates a variable fromlitre
s constructor, and passes it towater
s constructor.(name, age)my::player
creates a variable from typeplayer
with(name, age)
constructor.()m4gun
creates a variable from typem4gun
with default constructor.
2. Suggestion Detail
Currently, Cpp2 doesn't have a special syntax to directly call constructors. I suggest to directly call Constructors of a type in the form of Literal Suffixes. Let's name it Direct Object Construction Syntax or Constructor Call Syntax.
So ...TYPE
will be a syntactic sugar to (: TYPE = ...)
in Cpp2. For example:
//: = (: something = 2);
x0: = 2something;
//: = (: taip = ("text", 0));
x1: = ("text", 0)taip;
It requires to remove all built-in literal prefixes and suffixes:
- Remove
l
,ll
,ul
,ull
suffixes from integer literals. - Remove
f
,l
,f8
,f16
,f32
,f64
suffixes from floating-point literals. - Remove
u
,U
,u8
,u16
,u32
prefixes from both character and string literals.- Also removing
R
and$
prefixes from string literals as described in this issue, will make all literals to be consistent.
- Also removing
Constructors and UDLs (user-defined language literals) are two ways in Cpp1 to create objects from literals:
// -- It calls the constructor of `something`,
// -- therefore it needs parenthesis in Cpp1, otherwise it would be `something1` which is an identifier!
something(1)
// -- It doesn't call the constructor of `something`, because it's UDL.
1something
The following expression in Cpp2 satisfies the purpose of both two lines above:
// -- It calls the constructor of `something`,
// -- but it doesn't need parenthesis in Cpp2, because it looks like UDLs with somehow stronger behaviour.
1something
These are some notes to consider:
- Type aliases make this suggestion, easier, simpler, readable and a replacement for UDLs. For example:
ul: type == ulong;
s8: type == std::u8string;
a: = 1'000ul; // -- It's equal to 1'000ul in Cpp1.
b: = "text"s8; // -- It's equal to u8"text" in Cpp1.
- They can be within namespaces, because they are types:
// -- `box` is a type within namespace `my`.
x: = 2my::box;
- Multiple constructors (aka UDLs) can be applied to literals. For example:
// -- `litre` and `water` are types.
c: = (2.5litre)water;
- They can be applied to multiple literals, as they are arguments to call the constructor. For example:
// -- `player` is the type within namespace `my`.
p: = (name, age)my::player;
- They can call default constructor with
()
. For example:
// -- `m4gun` is the type.
x: = ()m4gun;
- They can be used with other operators. For example:
a: = 1'000ul * 5.0float;
b: = "text"s8.size();
c: = (2.5litre)water.weight();
d: = ((1my::box)apple + (2my::box)orange).weight();
name: = ask_player_name();
age: = ask_player_age();
p: = (name, age)my::player;
p.buy(()m4gun, 30bullet);
If Cpp2 would have array literals as described in this issue, a similar syntax would be available to call the constructor for them. For example in a consistent way with other literals, parentheses around [...]
aren't necessary:
//: = (: std::vector<int> = [1, 2, 3]);
x0: = [1, 2, 3]std::vector<int>;
dict: <T> type = std::vector<std::pair<std::string, T>>;
//: = (: dict<int> = [("a", 1), ("b", 2)]);
y0: = [("a", 1), ("b", 2)]dict<int>;
Consider how ...TYPE
is expressive and more readable than (: TYPE = ...)
, that's the reason why Cpp1 has UDLs. For example:
//: = (: point<int> = (1, 2)) * (: std::vector<int> = [1, 2, 3]);
ab: = (1, 2)point<int> * [1, 2, 3]std::vector<int>;
//: =(: box = ((: apple = 10) + (: orange = 20)));
mn: = (10apple + 20orange)box;
//: = player.buy((: apple = (: kg = 1)), (: health = (: box = 2)));
uv: = player.buy(2gun, (2box)health);
It's possible to consume Cpp1 UDLs. For example:
// -- `ms` is Cpp1 UDL.
//: = (: my::clock = (operator""ms(: ulonglong = 10)));
ab: = ((10ulonglong)ms)my::clock;
Constructors can replace Cpp1 UDLs completely, but Cpp2 can still support to author UDLs (user-defined literal suffixes, e.g. operator""suffix
). Probably the plan is to only consume UDLs as described in this comment from @hsutter.
3. Your Questions
Will your feature suggestion eliminate X% of security vulnerabilities of a given kind in current C++ code?
No.
Will your feature suggestion automate or eliminate X% of current C++ guidance literature?
Yes, because this change makes Cpp2 to reduce the concept count with a general language feature. So it will be simpler to learn and understand which leads to smaller guidance literature.
- It unifies constructors with UDLs. They are semantically the same. Both of them create a new object.
- It's useful in generic programming.
- It reduces concept count.
- Novice programmers don't need to learn a distinct concept about UDLs.
- All types benefit from UDL like syntax. It's not needed to declare UDL for them.
- It eliminates the need of understanding and learning built-in prefixes and suffixes for literals.
- The syntax of calling constructors will be expressive and readable.
- It distincts constructors from regular function calls. They are semantically different.
- Constructors:
...TYPE
, parentheses are not necessary when...
is only one literal.()TYPE
, it calls the default constructor(args...)TYPE
- Regular Function Calls:
FUNCTION()
, it calls a function without argumentsFUNCTION(args...)
obj.FUNCTION()
obj.FUNCTION(args...)
- Constructors:
- They can be chained together, whereas it's not possible with UDLs in Cpp1.
- Only one UDL can be applied to a literal in Cpp1.
- Constructors already can be templated, but UDLs cannot be templated.
- UDL templates are not supported in Cpp1.
- It removes built-in literal prefixes and suffixes. They are inconsistent and redundant.
- They are visually inconsistent.
- Some of them are prefix.
- Some of them are suffix.
- Their behaviours are inconsistent when the constant of literal exceeds the type as described in this comment.
- They are visually inconsistent.
- The name to construct a literal and to declare a variable will be consistently the same.
- It's not needed to declare a new name for literal suffixes.
- The name of types are like a suffix that will construct an object.
- They can be applied to literals with qualified name (if they are within namespaces) unlike UDLs which need
using
statement before they can be applied to literals.- That's why UDLs in Cpp1 have to be prefixed with
_
, thus they will be distinguished from UDLs which are declared in the Cpp1 standard library.
- That's why UDLs in Cpp1 have to be prefixed with
4. More Examples
By declaring type aliases to have familiar names:
ul: type == ulong;
ull: type == ulonglong;
s8: type == std::u8string;
x: ull = 2 + 2ul + 2ull;
y: = (0, 0)point + (0, 0)point;
call((0, 0)point, "text"s8.size());
m: = my::http::download("http://somewhere/somefile.ext"url.encode());
// -- `min` and `s` are Cpp1 UDLs from `std::chrono`.
n: = (1min + 10s)my::clock;
The process of object constructions will be simpler and readable:
p1: player = ("Sam"id, 1year);
p2: player = (112id, 2year);
p3: player = ((114)id, 1year + 4month);
p4: player = (("Alex", 110)id, 3year + 3month);
((p2, p3)team, (p1, p4)team)battle.start();
5. Considered Alternatives
This suggestion is a simpler and generalized alternative way to both this issue and this issue, with a different approach. This suggestion completely unifies literal suffixes with constructors instead of integrating them with templates.
Edits
- Haskell is a better language for syntax highlighting my Cpp2 examples! 😅