Discussion:
std::degenerate<> class template : primitive/enum types with a special "undefined" value
(too old to reply)
t***@gmail.com
2013-09-01 08:43:07 UTC
Permalink
Hello,

I am sending this idea for feedback from the community. I don't know if any
prior work has been down on this, or if this can be done with
std::optional. If so please let me know.

Let's say that you want to implement the Tic-Tac-Toe game, and you want to
store the state of the board into an array of 9 values. The C way to do it
would be:

enum SquareState { EMPTY, CROSS, NOUGHT };
SquareState board[9];

The C++98 way to do it would be:

enum SquareState { EMPTY, CROSS, NOUGHT };
std::vector<SquareState> board(9);

The C++11 way would add:

enum class SquareState { EMPTY, CROSS, NOUGHT };
std::array<SquareState, 9> board;

Now, you're happy. Your code is a lot cleaner, but then, you have to store
the player-to-play. What type will you use? Since you have to associate
either CROSS or NOUGHT to each player, you would use the same constant.

enum class SquareState { EMPTY, CROSS, NOUGHT };
std::array<SquareState, 9> board;
SquareState player_to_play;

Now, your spider sense tells you that player_to_play should not be a
"SquareState". That should be the other way around.

enum class Player { EMPTY, CROSS, NOUGHT };
std::array<Player, 9> board;
Player player_to_play;

But now: who is this Player::EMPTY person? And then, you remember that you
really like the work of the C++ community, and upgrade to experimental
C++14 :

enum class Player { CROSS, NOUGHT };
std::array<std::optional<Player>, 9> board;
Player player_to_play;

You like it better. But you realize that this may have substantially
increased demand in memory. Indeed, optional effectively adds "undefined"
to the set of values already encodable by its template parameter. Indeed,
if you compare sizeof's of Player and std::optional<Player>, you get :

Size of Player:4
Size of optional<Player>:8

Even setting the enum storage type to char gives:

Size of Player:1
Size of optional<Player>:2

And this makes you the laughing stock of the C programmers, even with the
semantic improvement, because you're using two bytes to encode a set of 3
values.

The proposal would be of a class template which:

- Would be a drop-in replacement for optional, i.e. have the same
interface and close enough semantics,
- Would set apart a value from the encodable set, and treat it as
undefined.

For example :

template<class T, T uninitialized_value>
class degenerate {
T value;
public:
degenerate(T v = uninitialized_value)
: value(v) {}

T& operator* () {
if(value == uninitialized_value) {
throw std::logic_error();
}

return value;
}

// ...
};

Which gives effectively :

Size of Player:1
Size of optional<Player>:2
Size of degenerate<Player, Player(-1)>:1

I find lots of uses for this. What do you think?
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Maurice Bos
2013-09-01 16:06:05 UTC
Permalink
A similar situation occurs when combining optional<T> and variant<T...>:
optional<variant<int, float, string>>. The variant probably keeps some
integer (e.g. 0,1, or 2) to keep track of whether it contains a int, float
or a string. The optional could, instead of adding a boolean, somehow store
a value that has no meaning for the variant (e.g. -1 or 3) in that integer
when the variant is not set.

However, the degenerate<typename T, T> solution would not be a solution in
this case. (Maybe optional<> should be specialzed for varaiants, or
optional_variant should exist, or variant<void, int, float, string> shoud
be used instead, but I'm wondering if there might exist a solution for both
this situation and the situations degenerate<template T, T> solves, since
they are similar.)

-Maurice-
Post by t***@gmail.com
Hello,
I am sending this idea for feedback from the community. I don't know if
any prior work has been down on this, or if this can be done with
std::optional. If so please let me know.
Let's say that you want to implement the Tic-Tac-Toe game, and you want to
store the state of the board into an array of 9 values. The C way to do it
enum SquareState { EMPTY, CROSS, NOUGHT };
SquareState board[9];
enum SquareState { EMPTY, CROSS, NOUGHT };
std::vector<SquareState> board(9);
enum class SquareState { EMPTY, CROSS, NOUGHT };
std::array<SquareState, 9> board;
Now, you're happy. Your code is a lot cleaner, but then, you have to store
the player-to-play. What type will you use? Since you have to associate
either CROSS or NOUGHT to each player, you would use the same constant.
enum class SquareState { EMPTY, CROSS, NOUGHT };
std::array<SquareState, 9> board;
SquareState player_to_play;
Now, your spider sense tells you that player_to_play should not be a
"SquareState". That should be the other way around.
enum class Player { EMPTY, CROSS, NOUGHT };
std::array<Player, 9> board;
Player player_to_play;
But now: who is this Player::EMPTY person? And then, you remember that you
really like the work of the C++ community, and upgrade to experimental
enum class Player { CROSS, NOUGHT };
std::array<std::optional<Player>, 9> board;
Player player_to_play;
You like it better. But you realize that this may have substantially
increased demand in memory. Indeed, optional effectively adds "undefined"
to the set of values already encodable by its template parameter. Indeed,
Size of Player:4
Size of optional<Player>:8
Size of Player:1
Size of optional<Player>:2
And this makes you the laughing stock of the C programmers, even with the
semantic improvement, because you're using two bytes to encode a set of 3
values.
- Would be a drop-in replacement for optional, i.e. have the same
interface and close enough semantics,
- Would set apart a value from the encodable set, and treat it as
undefined.
template<class T, T uninitialized_value>
class degenerate {
T value;
degenerate(T v = uninitialized_value)
: value(v) {}
T& operator* () {
if(value == uninitialized_value) {
throw std::logic_error();
}
return value;
}
// ...
};
Size of Player:1
Size of optional<Player>:2
Size of degenerate<Player, Player(-1)>:1
I find lots of uses for this. What do you think?
--
---
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-proposals/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
t***@gmail.com
2013-09-01 18:35:46 UTC
Permalink
Indeed. The type T would be effectively constrained to whatever types are
permitted for non-type, non-template template parameters. (Standard §14.1/4)

These are :

- integral or enumeration type,
- pointer to object or pointer to function,
- lvalue reference to object or lvalue reference to function,
- pointer to member,
- std::nullptr_t

Among these, I think that only lvalue reference to object would have
EXTREMELY WEIRD behaviour, and would have to be explicitly excluded.

I think that the main uses of std::degenerate would be on integral and enum
types, where the default value should be other than the zero of the type.
Post by Maurice Bos
optional<variant<int, float, string>>. The variant probably keeps some
integer (e.g. 0,1, or 2) to keep track of whether it contains a int, float
or a string. The optional could, instead of adding a boolean, somehow store
a value that has no meaning for the variant (e.g. -1 or 3) in that integer
when the variant is not set.
However, the degenerate<typename T, T> solution would not be a solution in
this case. (Maybe optional<> should be specialzed for varaiants, or
optional_variant should exist, or variant<void, int, float, string> shoud
be used instead, but I'm wondering if there might exist a solution for both
this situation and the situations degenerate<template T, T> solves, since
they are similar.)
-Maurice-
Post by t***@gmail.com
Hello,
I am sending this idea for feedback from the community. I don't know if
any prior work has been down on this, or if this can be done with
std::optional. If so please let me know.
Let's say that you want to implement the Tic-Tac-Toe game, and you want
to store the state of the board into an array of 9 values. The C way to do
enum SquareState { EMPTY, CROSS, NOUGHT };
SquareState board[9];
enum SquareState { EMPTY, CROSS, NOUGHT };
std::vector<SquareState> board(9);
enum class SquareState { EMPTY, CROSS, NOUGHT };
std::array<SquareState, 9> board;
Now, you're happy. Your code is a lot cleaner, but then, you have to
store the player-to-play. What type will you use? Since you have to
associate either CROSS or NOUGHT to each player, you would use the same
constant.
enum class SquareState { EMPTY, CROSS, NOUGHT };
std::array<SquareState, 9> board;
SquareState player_to_play;
Now, your spider sense tells you that player_to_play should not be a
"SquareState". That should be the other way around.
enum class Player { EMPTY, CROSS, NOUGHT };
std::array<Player, 9> board;
Player player_to_play;
But now: who is this Player::EMPTY person? And then, you remember that
you really like the work of the C++ community, and upgrade to experimental
enum class Player { CROSS, NOUGHT };
std::array<std::optional<Player>, 9> board;
Player player_to_play;
You like it better. But you realize that this may have substantially
increased demand in memory. Indeed, optional effectively adds "undefined"
to the set of values already encodable by its template parameter. Indeed,
Size of Player:4
Size of optional<Player>:8
Size of Player:1
Size of optional<Player>:2
And this makes you the laughing stock of the C programmers, even with the
semantic improvement, because you're using two bytes to encode a set of 3
values.
- Would be a drop-in replacement for optional, i.e. have the same
interface and close enough semantics,
- Would set apart a value from the encodable set, and treat it as
undefined.
template<class T, T uninitialized_value>
class degenerate {
T value;
degenerate(T v = uninitialized_value)
: value(v) {}
T& operator* () {
if(value == uninitialized_value) {
throw std::logic_error();
}
return value;
}
// ...
};
Size of Player:1
Size of optional<Player>:2
Size of degenerate<Player, Player(-1)>:1
I find lots of uses for this. What do you think?
--
---
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-proposals/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
t***@gmail.com
2013-09-01 18:47:24 UTC
Permalink
Post by Maurice Bos
optional<variant<int, float, string>>. The variant probably keeps some
integer (e.g. 0,1, or 2) to keep track of whether it contains a int, float
or a string. The optional could, instead of adding a boolean, somehow store
a value that has no meaning for the variant (e.g. -1 or 3) in that integer
when the variant is not set.
However, the degenerate<typename T, T> solution would not be a solution in
this case. (Maybe optional<> should be specialzed for varaiants, or
optional_variant should exist, or variant<void, int, float, string> shoud
be used instead, but I'm wondering if there might exist a solution for both
this situation and the situations degenerate<template T, T> solves, since
they are similar.)
If an optional_variant<> should exist, maybe degenerate<> could be a
building block for the selector. I'm not sure. The uses I see for
degenerate<> are :

- for all the cases of integral or enum types where the programmer
defines an extra symbolic constant to represent the undefined-ness,
- to store a pointer value, and give the undefinedness semantic to
nullptr.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Vicente J. Botet Escriba
2013-09-01 21:58:54 UTC
Permalink
* Would be a drop-in replacement for optional, i.e. have the same
interface and close enough semantics,
* Would set apart a value from the encodable set, and treat it as
undefined.
|
template<classT,T uninitialized_value>
classdegenerate {
T value;
degenerate(T v =uninitialized_value)
:value(v){}
T&operator*(){
if(value ==uninitialized_value){
throwstd::logic_error();
}
returnvalue;
}
// ...
};
|
Size of Player:1
Size of optional<Player>:2
Size of degenerate<Player, Player(-1)>:1
I find lots of uses for this. What do you think?
Hi,

I think that this class could be used as an excellent building block for
an optimized optional implementation, when e.g. a given trait
is_degenerated<T> is true_type. In this case the optional<t>
implementation coul dbe based on degenerate<T, degenerate_value<T>::value>.

In, your example, if the following was declared

template <>
struct is_degenerate<Player> : true_type {};
template <>
struct degenerate_value<Player> : integral_constant<Player, Player(-1)> {};

sizeof (optional<Player>) could be equal to sizeof(Player).

The standard would not need to include the class degenerate, but only
the traits is_degenerate and degenerate_value and some constraints on
the sizeof optional<T> when is_degenerate<T> is true_type.

Best,
Vicente
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
t***@gmail.com
2013-09-02 04:44:47 UTC
Permalink
Vicente,

I think that this class could be used as an excellent building block for an
Post by Vicente J. Botet Escriba
optimized optional implementation, when e.g. a given trait
is_degenerated<T> is true_type. In this case the optional<t> implementation
coul dbe based on degenerate<T, degenerate_value<T>::value>.
I thought about this. But degenerate does not have the exact same
semantics as optional. Indeed:

- given a type T containing n values, the type optional<T> effectively
contains n + 1 values.
- given a type T containing n values, the type degenerate <T, v> still
contains n values, and actually implements optional<T - {n}>

In, your example, if the following was declared
Post by Vicente J. Botet Escriba
template <>
struct is_degenerate<Player> : true_type {};
template <>
struct degenerate_value<Player> : integral_constant<Player, Player(-1)> {};
sizeof (optional<Player>) could be equal to sizeof(Player).
The standard would not need to include the class degenerate, but only the
traits is_degenerate and degenerate_value and some constraints on the
sizeof optional<T> when is_degenerate<T> is true_type.
I think specifying the degenerate_value as part of the variable type is
useful. You may be implementing a protocol which, at two places, uses two
different values for indicating absence of a specific data, e.g. "coffee
type" being REGULAR = 1, DECAF = 2, ESPRESSO = 3, and at some place the
"undefined" value is 0, and at another place the undefined value is -1.

By the way, I did not know about integral_constant. Thank you.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
David Krauss
2013-09-02 04:05:31 UTC
Permalink
Would set apart a value from the encodable set, and treat it as undefined.
What value?
Size of degenerate<Player, Player(-1)>:1
Ohh -1. Of course. And I'm supposed to specify that it's -1. Wonderful.
I find lots of uses for this. What do you think?
I think that degenerate is nothing like the enumeration it is supposed to
adapt. It's a class type, for starters.

I think that enumerations should be able to inherit from other enumerations
as an underlying type, such as to be able to represent supersets. This is
an extremely common use case and isn't limited in general to just one extra
"none" enumerator.

Following the degenerate pattern, the user could keep adding ever-more
degenerate cases. What if we want to represent graphical tiles outside the
playing area, now we have X, O, "no player," and "not on the board," with
the latter two not having proper names at all, but relying on the reader's
inference about what exact set the enumeration (or non-enumeration) is
supposed to cover.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
t***@gmail.com
2013-09-02 05:13:08 UTC
Permalink
David,

What value?
Ohh -1. Of course. And I'm supposed to specify that it's -1. Wonderful.
Of course I'm supposed to specify it. With current state of art, in C++,
you can only encode values within sets whose cardinality is a power of 2.
You can't specify a type like { REGULAR, DECAF, ESPRESSO } and store it to
a 3-state location. The bare minimum is two bits, which can encode 4 values.

In my example, the enum class Player (let's assume I specified "char"
storage) takes its values from a set whose cardinality is 256. I specified
only two symbolic constants for values 0 and 1. (resp. CROSS and NOUGHT).
Player(-1) is a perfectly valid Player value, just like Player(124). These
do not get symbolic constant support in my program, that's it.
Post by David Krauss
I think that degenerate is nothing like the enumeration it is supposed to
adapt.
So do I. optional<int> is nothing like int. It not supposed to "adapt" the
enumeration. It is supposed to give optional semantics to my enum, for
which I will not use all 256 values.

It's a class type, for starters.
degenerate<T> is POD. "class" means "user-defined type"
Post by David Krauss
I think that enumerations should be able to inherit from other
enumerations as an underlying type, such as to be able to represent
supersets. This is an extremely common use case and isn't limited in
general to just one extra "none" enumerator.
Indeed. MAX_VALUE, NB_FLAGS, etc. I too would wish for better compiler
support for enums. I would wish to have some type traits that give me the
number of defined symbolic constants, what is the min, max constant, .

Following the degenerate pattern, the user could keep adding ever-more
Post by David Krauss
degenerate cases. What if we want to represent graphical tiles outside the
playing area, now we have X, O, "no player," and "not on the board," with
the latter two not having proper names at all, but relying on the reader's
inference about what exact set the enumeration (or non-enumeration) is
supposed to cover.
Indeed. It is supposed to mimic optional<T> semantics, which has only one
"degenerate" case.

Laurent
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
David Krauss
2013-09-02 05:42:44 UTC
Permalink
Post by t***@gmail.com
David,
What value?
Ohh -1. Of course. And I'm supposed to specify that it's -1. Wonderful.
Of course I'm supposed to specify it. With current state of art, in C++,
you can only encode values within sets whose cardinality is a power of 2.
You can't specify a type like { REGULAR, DECAF, ESPRESSO } and store it to
a 3-state location. The bare minimum is two bits, which can encode 4 values.
So it turns out that it's not "not part of the encodable set" after all.
Post by t***@gmail.com
In my example, the enum class Player (let's assume I specified "char"
storage) takes its values from a set whose cardinality is 256. I specified
only two symbolic constants for values 0 and 1. (resp. CROSS and NOUGHT).
Player(-1) is a perfectly valid Player value, just like Player(124). These
do not get symbolic constant support in my program, that's it.
Now you have to be concerned with encoding, both in selection of the
underlying type and the extra state-value, which violates separation of
concerns.

Not only don't you have symbolic support for one value (or N values,
however many times this concept is applied), you don't have access to any
interface designed for the underlying enumeration, except by implicit
conversion.

I think that degenerate is nothing like the enumeration it is supposed to
Post by t***@gmail.com
Post by David Krauss
adapt.
So do I. optional<int> is nothing like int. It not supposed to "adapt" the
enumeration. It is supposed to give optional semantics to my enum, for
which I will not use all 256 values.
Adding a state machine construct to an int is different from adding a state
to a state machine.
Post by t***@gmail.com
It's a class type, for starters.
degenerate<T> is POD. "class" means "user-defined type"
Enumerations are user-defined types which are not classes.
Post by t***@gmail.com
I think that enumerations should be able to inherit from other
Post by David Krauss
enumerations as an underlying type, such as to be able to represent
supersets. This is an extremely common use case and isn't limited in
general to just one extra "none" enumerator.
Indeed. MAX_VALUE, NB_FLAGS, etc. I too would wish for better compiler
support for enums. I would wish to have some type traits that give me the
number of defined symbolic constants, what is the min, max constant, .
Language support, not compiler support. You can do already plenty by
defining traits on your own enumeration types, and following conventions,
but some conventions and traits would need formalization before the
language can move forward.
Post by t***@gmail.com
Following the degenerate pattern, the user could keep adding ever-more
Post by David Krauss
degenerate cases. What if we want to represent graphical tiles outside the
playing area, now we have X, O, "no player," and "not on the board," with
the latter two not having proper names at all, but relying on the reader's
inference about what exact set the enumeration (or non-enumeration) is
supposed to cover.
Indeed. It is supposed to mimic optional<T> semantics, which has only one
"degenerate" case.
I'm a bit skeptical about use of optional for persistent state
representation. It's a good placeholder for an object that might be
temporarily unavailable, but it's not a good idiom for a real state machine.

Your use case describes a state machine, which should have a coherent
representation, not patched together piecemeal. State machines invite
spaghetti code. Consistent use of degenerate will surely lead ultimately to
uncertain ownership of "outside" values, overloading them and producing
semantic collisions where different notions of "outside" have exactly the
same compile-time and runtime representation.
Post by t***@gmail.com
Laurent
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
t***@gmail.com
2013-09-03 05:17:52 UTC
Permalink
David,

I think we may not understand each other. I did not talk about state
machine, and I did say that the value WAS part of the encodable set.

So that we can have a common ground of discussion, here is a complete
implementation of the template:

template<class T, T uninitialized_value>
class degenerate {
T value_;

constexpr bool initialized() const {
return value_ != uninitialized_value;
}

void clear() {
value_ = uninitialized_value;
}

void assert_initialized() {
_assert(initialized(), "Access to uninitialized degenerate.");
}
public:
typedef T value_type;

// 1 Constructors
constexpr degenerate(const T& v = uninitialized_value) noexcept
: value_(v) {}
constexpr degenerate(nullopt_t) noexcept
: degenerate() {}

constexpr degenerate(const degenerate& ) noexcept = default;

// 3 Assignment
degenerate& operator=(nullopt_t) noexcept
{
clear();
return *this;
}

degenerate& operator=(const degenerate&) noexcept = default;

// 4 Swap
void swap(degenerate& rhs) noexcept
{
using std::swap;
swap(value_, rhs.value_);
}

// 5 Observers
constexpr T const operator ->() const {
assert_initialized();
return value_;
}

T operator ->() {
assert_initialized();
return value_;
}

constexpr T const& operator *() const {
assert_initialized();
return value_;
}

T& operator *() {
assert_initialized();
return value_;
}

constexpr T const& value() const {
return initialized() ? value_ : (throw std::logic_error("bad optional
access"), value_);
}

T& value() {
return initialized() ? value : (throw std::logic_error("bad optional
access"), value);
}

constexpr explicit operator bool() const noexcept { return initialized();
}
};


Let me know if this was what you were thinking about.

Laurent
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
David Krauss
2013-09-03 06:40:16 UTC
Permalink
Post by t***@gmail.com
David,
I think we may not understand each other. I did not talk about state
machine, and I did say that the value WAS part of the encodable set.
Sorry, I initially misunderstood "set apart a value from the encodable set"
as something like "set a value apart from the encodable set."

Let me know if this was what you were thinking about.
There is no other misunderstanding here. The premise is that given a closed
set, you might want to add a member of the set to represent "other," thus
producing a new set. That's a poor programming paradigm because it excludes
conceptual precision and fails to anticipate that the same concept can
still apply to the new set. (That is the nature of set arithmetic.)
Enumerations are unfortunately not extensible, so it's best to define the
largest superset all at once. When that's impossible, ugly conversions are
necessary, but it's still important to use care in enumerating members of
supersets. Giving two completely separate software components an
opportunity to define the same value (of the same, somewhat verbose type)
to mean different things is the opposite of what we want.

What does all the above code actually *gain* over just casting to char and
assigning some crazy value through the cast? All the brackets and
conversions are just creating a false sense of legitimacy.

I can understand the value of an optimized optional. I've personally abused
floating-point infinity for such semantics (very carefully!). But that
should be approached as a question of how to let the user specify to
std::optional that the optimization should apply. Then there is no new
interface. (And, IMO, the optimization should apply to floating-point types
as well, whose values cannot be passed as template non-type parameters.)

Another approach could look like struct without_value< E, T = E::type > {
typedef T type; … }; where T is either a numeric type or has a member typewhich is numeric, and
E::value is the excluded value. (Such a definition would support a single
std::integral_constant argument.) This could assert to a container or
algorithm that the given value will not be used, and the given type should
be allocated. It would be a template flag rather than a proxy with tricky
conversion semantics — without_value would not be ODR-used, but ODR-use
would be the inevitable result of passing it somewhere it's not supported.
It would support floating-point types and NaN. And it would actually be a
numeric invariant; the concept could extend to other hints such as that an
argument must be in a numeric range. A generic way to pass numeric hints to
algorithms and containers is something currently lacking, but probably
helpful to scientific applications.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
David Krauss
2013-09-03 06:45:49 UTC
Permalink
without_value would not be ODR-used, but ODR-use would be the inevitable
result of passing it somewhere it's not supported.
ODR-use is not what I meant. Appearing in a nested-name-specifier is
ODR-use. It could not be constructed. The point is that if used
incorrectly, the user should get a static_assert or other diagnostic saying
that it's not applicable there.

I haven't contemplated correct usage, aside from std::optional.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
t***@gmail.com
2013-09-03 12:18:04 UTC
Permalink
Post by David Krauss
Enumerations are unfortunately not extensible
I think the problem is not in what you're saying here, but in the cause
thereof. A variable of a C++ enumeration can have any encodable value, not
just the ones you list in the enum [class] definition. Therefore, they
cannot be extensible. And the set of their values is already taking all the
space available in the storage type.

In C++, except bool, there is no built-in type whose set of values is
strictly smaller than its storage capacity, and enums make no exception.
The only current way to go is a class.

What does all the above code actually *gain* over just casting to char and
Post by David Krauss
assigning some crazy value through the cast? All the brackets and
conversions are just creating a false sense of legitimacy.
Here are the actual gains :

1. There is a semantic gain to saying that "the board is a thing with 9
distinguishable squares that MAY bear a token, CROSS or NOUGHT".
enum class Token { CROSS, NOUGHT };

std::vector<std::optional<Token>> board(9);

Instead of:

enum class Token { NONE, CROSS, NOUGHT };

std::vector<Token> board(9);

Too bad it takes so much memory.

2. Zero-initialisation : There is a difference between :

enum class Token { NONE, CROSS, NOUGHT };

std::vector<Token> board(9);

And

enum class Token { CROSS, NOUGHT, NONE };

std::vector<Token> board(9);

Only the first one does the right thing, and the following one also:

enum class Token { CROSS, NOUGHT };

std::vector<std::optional<Token>> board(9);

So does the following :

enum class Token { CROSS, NOUGHT };

std::vector<degenerate<Token, Token(-1)>> board(9);

3. You make it explicit in the type that you are abusing one of the values,
instead of littering your code with a symbolic constant.

I can understand the value of an optimized optional.
So do I, but only Vicente said that it was an optimized optional. I said
that it should be a "drop-in replacement" for it. degenerate<T,v> actually
implements an equivalent of std::optional<T - {v}>, in the case of when
you're not using all the values of a type.
Post by David Krauss
But that should be approached as a question of how to let the user specify
to std::optional that the optimization should apply. Then there is no new
interface. (And, IMO, the optimization should apply to floating-point types
as well, whose values cannot be passed as template non-type parameters.)
It is impossible to steal a value from a type whose set of values take up
all the storage space allocated to the type, and still implement the right
semantics for optional<T>. Only bool is a "sparse" type, if I may say so.
Only std::optional<bool> may benefit from such an optimization.
Post by David Krauss
Another approach could look like struct without_value< E, T = E::type > {
typedef T type; … }; where T is either a numeric type or has a member typewhich is numeric, and
E::value is the excluded value. (Such a definition would support a single
std::integral_constant argument.) This could assert to a container or
algorithm that the given value will not be used, and the given type should
be allocated. It would be a template flag rather than a proxy with tricky
conversion semantics — without_value would not be ODR-used, but ODR-use
would be the inevitable result of passing it somewhere it's not supported.
It would support floating-point types and NaN. And it would actually be a
numeric invariant; the concept could extend to other hints such as that an
argument must be in a numeric range. A generic way to pass numeric hints to
algorithms and containers is something currently lacking, but probably
helpful to scientific applications.
These are too few words for such a lot of thinking :) If I may summarize,
would you say that C++ lacks the ability to easily create "sparse" types.
(with the above ad hoc definition of "sparse") and as such cannot reason on
the "actually used set of values of types"?
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Nicol Bolas
2013-09-03 12:47:43 UTC
Permalink
Post by David Krauss
Enumerations are unfortunately not extensible
I think the problem is not in what you're saying here, but in the cause
thereof. A variable of a C++ enumeration can have any encodable value, not
just the ones you list in the enum [class] definition. Therefore, they
cannot be extensible.
I fail to see how those last two sentences have anything to do with one
another.

Are you legally allowed to cast an integer to a value of enum type? Yes,
assuming it fits into the underlying type. The set of integers that could
be stored in an enumeration have nothing to do with what the enumerator
values are.

So why does the fact that an enum type could take on values outside of the
enumerators have anything to do with whether or not the list of enumerators
for an enum type could partially come from another enum type?

3. You make it explicit in the type that you are abusing one of the values,
Post by David Krauss
instead of littering your code with a symbolic constant.
If that symbolic constant were actually *part* of the enumeration, it
wouldn't be "abusing" anything. You're only abusing it because you have no
other way to achieve what you want.
Post by David Krauss
I can understand the value of an optimized optional.
So do I, but only Vicente said that it was an optimized optional.
Whether you want to admit it or not, it serves the same functional purpose
as optional. It creates a type which may or may not be considered to have a
valid value of some specific type. It has a value which is considered to be
"not a real value". It has value semantics. And so forth.

Indeed, I would go so far as to say that the (unnecessary) differences in
interface between your class and `optional` are *detriments* of your class,
not strengths. It's a half-finished optional that lacks many of the traits
of the real class.

I said that it should be a "drop-in replacement" for it. degenerate<T,v>actually implements an equivalent of std::optional<T
Post by David Krauss
- {v}>, in the case of when you're not using all the values of a type.
Post by David Krauss
But that should be approached as a question of how to let the user
specify to std::optional that the optimization should apply. Then there
is no new interface. (And, IMO, the optimization should apply to
floating-point types as well, whose values cannot be passed as template
non-type parameters.)
It is impossible to steal a value from a type whose set of values take up
all the storage space allocated to the type, and still implement the right
semantics for optional<T>.
How do you figure that?

If you're making a space-optimized `optional` variation, then by definition
it would *not* be the same type as `optional<T>`. It could use the same
name: `optional<T, ...>`, where ... is whatever you do to denote the value
that represents "not a value". It could be some kind of functor, a type
that provides constexpr functions, or whatever.

The point is that `optional<T>` would have no more relation to `optional<T,
...>` than it would to `optional<U>`. So there would be no problem with
stealing a value for `optional<T>` because that's not how an `optional<T>`
would ever work. Only the optimized form would "steal a value".

Another approach could look like struct without_value< E, T = E::type > {
Post by David Krauss
Post by David Krauss
typedef T type; … }; where T is either a numeric type or has a member
type which is numeric, and E::value is the excluded value. (Such a
definition would support a single std::integral_constant argument.) This
could assert to a container or algorithm that the given value will not be
used, and the given type should be allocated. It would be a template flag
rather than a proxy with tricky conversion semantics — without_valuewould not be ODR-used, but ODR-use would be the inevitable result of
passing it somewhere it's not supported. It would support floating-point
types and NaN. And it would actually be a numeric invariant; the concept
could extend to other hints such as that an argument must be in a numeric
range. A generic way to pass numeric hints to algorithms and containers is
something currently lacking, but probably helpful to scientific
applications.
These are too few words for such a lot of thinking :) If I may summarize,
would you say that C++ lacks the ability to easily create "sparse" types.
(with the above ad hoc definition of "sparse") and as such cannot reason on
the "actually used set of values of types"?
I see no reason why one should lead to the other. A type is whatever you
want it to be. If you want to create a type that behaves like an `int`, but
considers the number -1 to be "not a real value", you can. And you can
"reason" about such types just fine.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
t***@gmail.com
2013-09-03 14:02:48 UTC
Permalink
Nicol Bolas,

Pleased to meet you.
Post by t***@gmail.com
I think the problem is not in what you're saying here, but in the cause
Post by t***@gmail.com
thereof. A variable of a C++ enumeration can have any encodable value, not
just the ones you list in the enum [class] definition. Therefore, they
cannot be extensible.
I fail to see how those last two sentences have anything to do with one
another.
Are you legally allowed to cast an integer to a value of enum type? Yes,
assuming it fits into the underlying type. The set of integers that could
be stored in an enumeration have nothing to do with what the enumerator
values are.
Exactly. They are not related at all, that's why I'm saying that the set of
values of an enumerated type has the same cardinality as the set of values
of its underlying type. If it's obvious, sorry for stating it.
Post by t***@gmail.com
So why does the fact that an enum type could take on values outside of the
enumerators have anything to do with whether or not the list of enumerators
for an enum type could partially come from another enum type?
It depends if "extensible" means "the set of values can be extended" or
"the set of enumerators can be extended". In the context of the discussion
about degenerate<>, I am understanding the former. I agree with you that
assuming the latter definition of "extensible", the enums could be
"extensible" .

3. You make it explicit in the type that you are abusing one of the values,
Post by t***@gmail.com
Post by t***@gmail.com
instead of littering your code with a symbolic constant.
If that symbolic constant were actually *part* of the enumeration, it
wouldn't be "abusing" anything. You're only abusing it because you have no
other way to achieve what you want.
No. If the symbolic constant were actually one of the enumerators, the only
thing it would mean is that I would not have to cast from an int. To be
precise, in any case the value denoted by the symbolic constant HAS to be
part of the set of values of the enumeration.

I can write

enum class Token { CROSS, NOUGHT, NONE };
std::vector<std::degenerate<Token, Token::NONE>> board(9);


And it will have the right behaviour.
What bugs me is that NONE is not a token. It's like this old joke about
finding the "Any" key.
Post by t***@gmail.com
Post by t***@gmail.com
Indeed, I would go so far as to say that the (unnecessary) differences in
interface between your class and `optional` are *detriments* of your
class, not strengths. It's a half-finished optional that lacks many of the
traits of the real class.
Yes. Were it finished, there would be a proposal, not a discussion. What
traits does it lack exactly? What differences in interface do you detect
and how are they detriments?

I said that it should be a "drop-in replacement" for it. degenerate<T,v>actually implements an equivalent of std::optional<T
Post by t***@gmail.com
Post by t***@gmail.com
- {v}>, in the case of when you're not using all the values of a type.
Post by David Krauss
But that should be approached as a question of how to let the user
specify to std::optional that the optimization should apply. Then there
is no new interface. (And, IMO, the optimization should apply to
floating-point types as well, whose values cannot be passed as template
non-type parameters.)
It is impossible to steal a value from a type whose set of values take up
all the storage space allocated to the type, and still implement the right
semantics for optional<T>.
How do you figure that?
Because it would not implement the semantics of optional<T>, but that of optional<T
- {n}>.
Post by t***@gmail.com
If you're making a space-optimized `optional` variation, then by
definition it would *not* be the same type as `optional<T>`. It could use
the same name: `optional<T, ...>`, where ... is whatever you do to denote
the value that represents "not a value". It could be some kind of functor,
a type that provides constexpr functions, or whatever.
I thought about functors to accomodate floating-point and class types,
indeed.
Post by t***@gmail.com
Whether you want to admit it or not, it serves the same functional purpose
as optional. It creates a type which may or may not be considered to have a
valid value of some specific type. It has a value which is considered to be
"not a real value". It has value semantics. And so forth.
<snip/>
The point is that `optional<T>` would have no more relation to `optional<T,
Post by t***@gmail.com
...>` than it would to `optional<U>`. So there would be no problem with
stealing a value for `optional<T>` because that's not how an `optional<T>`
would ever work. Only the optimized form would "steal a value".
Nonetheless, this would not be equivalent to an optimized optional<T>, but
to an optimized optional<T - { n }>. They would not be the same type, but
would not even implement the same concept.
Post by t***@gmail.com
I see no reason why one should lead to the other. A type is whatever you
want it to be. If you want to create a type that behaves like an `int`, but
considers the number -1 to be "not a real value", you can. And you can
"reason" about such types just fine.
I can reason about it extremely fine. The compiler can't, and can't decide
on its own to steal a value of any built-in type, enumeration, or class
except bool. (I mean that you won't be able to write a template performing
this)
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Nicol Bolas
2013-09-03 22:49:56 UTC
Permalink
Post by t***@gmail.com
Post by Nicol Bolas
Post by Nicol Bolas
Indeed, I would go so far as to say that the (unnecessary) differences
in interface between your class and `optional` are *detriments* of your
class, not strengths. It's a half-finished optional that lacks many of the
traits of the real class.
Yes. Were it finished, there would be a proposal, not a discussion. What
traits does it lack exactly? What differences in interface do you detect
and how are they detriments?
Just look at `optional`. All of that.

I said that it should be a "drop-in replacement" for it. degenerate<T,v>actually implements an equivalent of std::optional<T
Post by t***@gmail.com
Post by Nicol Bolas
Post by Nicol Bolas
- {v}>, in the case of when you're not using all the values of a type.
Post by David Krauss
But that should be approached as a question of how to let the user
specify to std::optional that the optimization should apply. Then
there is no new interface. (And, IMO, the optimization should apply to
floating-point types as well, whose values cannot be passed as template
non-type parameters.)
It is impossible to steal a value from a type whose set of values take
up all the storage space allocated to the type, and still implement the
right semantics for optional<T>.
How do you figure that?
Because it would not implement the semantics of optional<T>, but that of optional<T
- {n}>.
... yes. That's the point of the space optimized optional type. It's
exactly like `optional<T>`, only certain values are considered "not a real
value".

I see no reason to give it a special name just because of that *minor*difference between them.
Post by t***@gmail.com
If you're making a space-optimized `optional` variation, then by
Post by Nicol Bolas
definition it would *not* be the same type as `optional<T>`. It could
use the same name: `optional<T, ...>`, where ... is whatever you do to
denote the value that represents "not a value". It could be some kind of
functor, a type that provides constexpr functions, or whatever.
I thought about functors to accomodate floating-point and class types,
indeed.
Post by Nicol Bolas
Whether you want to admit it or not, it serves the same functional
purpose as optional. It creates a type which may or may not be considered
to have a valid value of some specific type. It has a value which is
considered to be "not a real value". It has value semantics. And so forth.
<snip/>
The point is that `optional<T>` would have no more relation to
Post by Nicol Bolas
`optional<T, ...>` than it would to `optional<U>`. So there would be no
problem with stealing a value for `optional<T>` because that's not how an
`optional<T>` would ever work. Only the optimized form would "steal a
value".
Nonetheless, this would not be equivalent to an optimized optional<T>,
but to an optimized optional<T - { n }>.
I fail to see the difference. By definition, an "optimized optional<T>" is
stealing a potential value from `T` and calling it "unengaged". That's what
the class means.
Post by t***@gmail.com
They would not be the same type, but would not even implement the same
concept.
How is it not? As previously stated, it is still conceptually *optional*.
You have a type that may or may not contain a real value. The only
difference is how you determine what is and is not a "real value".

I see no reason why one should lead to the other. A type is whatever you
Post by t***@gmail.com
Post by Nicol Bolas
want it to be. If you want to create a type that behaves like an `int`, but
considers the number -1 to be "not a real value", you can. And you can
"reason" about such types just fine.
I can reason about it extremely fine. The compiler can't, and can't decide
on its own to steal a value of any built-in type, enumeration, or class
except bool. (I mean that you won't be able to write a template performing
this)
Sure you can. It's quite simple:

template<typename T, typename NullValueProvider>
class optional
{
public:
constexpr optional() : t(NullValueProvider::getNullValue()) {}
constexpr optional(const T &_t) : t(_t) {}
constexpr optional(const nullopt_T &) :
t(NullValueProvider::getNullValue()) {}

explicit operator bool() {return NullValueProvider::isNullValue(t);}

constexpr T const* operator->() const
{
return &t; //As with the regular optional version, the return value is
undefined if not engaged.
}

constexpr const T &value() const
{
if(*this) return t;
throw bad_optional_access; //Throws when not engaged.
}

private:
T t;
};

You can fill in the rest of the methods similarly.
`NullValueProvider::getNullValue` and `NullValueProvider::isNullValue`
would have to be `constexpr` if T is a literal type. There will have to be
some kind of type determination based on whether T is literal to use
`constexpr` conditionally.

I guess you're going to ask what happens if you construct one with the
"null" value. Well, what happens is exactly what you *expect* to happen:
the optional is not engaged. That is what you wanted; that's half the point
of explicitly specifying one or more values to be not "real" values.

You can also have appropriate copy/move constructors to and from different
"null" values, which will behave reasonably. By "reasonably", I mean if the
optional is considered disengaged before the copy, then no matter *where*it is copied, it will still be disengaged. Copying an engaged optional to a
space-optimized optional of a different type *may* cause the optional to be
disengaged (just like setting a value may disengage it).

The compiler isn't deciding to "steal" any values; the *user* is.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
t***@gmail.com
2013-09-04 04:48:13 UTC
Permalink
Nicol Bolas,

Thank you for your latest response, your opinion a lot clearer now.

If I may reword your comments into short constructive suggestions, that
would mean :

- That the class is a worthwhile attempt at building something
standardizable, not a hack altogether.

Just look at `optional`. All of that.
- That I should more closely keep the "optional" interface.
Post by Nicol Bolas
... yes. That's the point of the space optimized optional type. It's
exactly like `optional<T>`, only certain values are considered "not a real
value".
I see no reason to give it a special name just because of that *minor*difference between them.
- That the difference in behaviour is acceptable, and should be proposed
as another base template of optional<> instead of another getting another
name altogether.
Post by Nicol Bolas
template<typename T, typename NullValueProvider>
class optional
{
constexpr optional() : t(NullValueProvider::getNullValue()) {}
constexpr optional(const T &_t) : t(_t) {}
t(NullValueProvider::getNullValue()) {}
explicit operator bool() {return NullValueProvider::isNullValue(t);}
constexpr T const* operator->() const
{
return &t; //As with the regular optional version, the return value is
undefined if not engaged.
}
constexpr const T &value() const
{
if(*this) return t;
throw bad_optional_access; //Throws when not engaged.
}
T t;
};
<snip/>
- That I should implement another base template using functors, to
accomodate values that are not allowed as non-type tempate arguments.

I guess you're going to ask what happens if you construct one with the
Post by Nicol Bolas
the optional is not engaged. That is what you wanted; that's half the point
of explicitly specifying one or more values to be not "real" values.
You can also have appropriate copy/move constructors to and from different
Post by Nicol Bolas
"null" values, which will behave reasonably. By "reasonably", I mean if the
optional is considered disengaged before the copy, then no matter *where*it is copied, it will still be disengaged. Copying an engaged optional to a
space-optimized optional of a different type *may* cause the optional to
be disengaged (just like setting a value may disengage it).
The compiler isn't deciding to "steal" any values; the *user* is.
Indeed.

My comment about the compiler deciding to steal the value was not about
degenerate<>, but a sidenote. It was actually to be read "What would it
take so that we could write a template that decided to steal a value from
the encoding space, that would not conflict with all other allowable values
of the type?". But that's a subject for another forum topic.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Vicente J. Botet Escriba
2013-09-04 06:43:48 UTC
Permalink
Post by t***@gmail.com
Nicol Bolas,
Thank you for your latest response, your opinion a lot clearer now.
If I may reword your comments into short constructive suggestions,
* That the class is a worthwhile attempt at building something
standardizable, not a hack altogether.
Just look at `optional`. All of that.
* That I should more closely keep the "optional" interface.
... yes. That's the point of the space optimized optional type.
It's exactly like `optional<T>`, only certain values are
considered "not a real value".
I see no reason to give it a special name just because of that
/minor/ difference between them.
* That the difference in behaviour is acceptable, and should be
proposed as another base template of optional<> instead of another
getting another name altogether.
template<typename T, typename NullValueProvider>
class optional
{
constexpr optional() : t(NullValueProvider::getNullValue()) {}
constexpr optional(const T &_t) : t(_t) {}
t(NullValueProvider::getNullValue()) {}
explicit operator bool() {return NullValueProvider::isNullValue(t);}
constexpr T const* operator->() const
{
return &t; //As with the regular optional version, the return
value is undefined if not engaged.
}
constexpr const T &value() const
{
if(*this) return t;
throw bad_optional_access; //Throws when not engaged.
}
T t;
};
<snip/>
* That I should implement another base template using functors, to
accomodate values that are not allowed as non-type tempate arguments.
What do you think about defining the degenerate class empty

template <typename T, typename DegenerateValue>
struct degenerate
{
typedef T value_type;
typedef DegenerateValue degenerate_value_type;
};

and use it to make specialization of optional

template <typename T, typename DegenerateValue>
struct optional <degenerate<T,DegenerateValue>>
{
// include here the degenerated specialization
}

In this way optional doesn't changes its interface (preserv only one
template parameter), and we are able to have

optional<T>

and

optional<degenerate<Player, integral_constant<Player, Player(-1)>>

Best,
Vicente
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
David Krauss
2013-09-04 09:11:03 UTC
Permalink
On Wednesday, September 4, 2013 2:43:48 PM UTC+8, Vicente J. Botet Escriba
Post by Vicente J. Botet Escriba
What do you think about defining the degenerate class empty
and use it to make specialization of optional
optional<degenerate<Player, integral_constant<Player, Player(-1)>>
Same as my suggestion above :v)

Except that if the order of parameters is reversed, then the type (e.g.
Player) may be retrieved from integral_constant.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
t***@gmail.com
2013-09-04 09:39:17 UTC
Permalink
David,
Post by David Krauss
Same as my suggestion above :v)
Except that if the order of parameters is reversed, then the type (e.g.
Player) may be retrieved from integral_constant.
Indeed. I was intended to give you credit for this is my response to
Vicente, but in the flow of more technical writing, I forgot. Please don't
think that I'm ignoring you :)

Laurent
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
t***@gmail.com
2013-09-04 09:35:13 UTC
Permalink
Vicente,

What do you think about defining the degenerate class empty
Post by Vicente J. Botet Escriba
template <typename T, typename DegenerateValue>
struct degenerate
{
typedef T value_type;
typedef DegenerateValue degenerate_value_type;
};
You're asking me what I think, so let me first sum up how I perceive the
problem now, considering the feedback I got up to now :

- The class template that I first called degenerate<> is indeed targeted
to be a more space-efficient optional<>, and should be a specialisation
thereof.
- Can the compiler (or can the template writer make the compiler)
automatically determine a sensible value that would not conflict with any
other value of the type?

Which would limit to two implementation types for optional<>. The first one
for "dense" types, i.e. whose set of values have the same cardinality as
the set of storable values of their storage, and as such, you need to
extend the storage capacity to accomodate the additional "undefined" value.
This is the current implementation of optional<>. Something like:

struct optional_storage {
bool is_defined;
union value {
dummy_t dummy;
unsigned char[] correctly_aligned_correctly_sized_storage;
}
}

And another one for "sparse" types, i.e. whose set of values has
cardinality strictly less than the cardinality of the set of storable
values of their storage. Example of such types defined in the standard, are
bool and nullptr_t. (having respectively 2 and 1 value, but much wider
storage capacity) Details aside, the storage could be defined as :

union optional_storage {
underlying_type storage;
type value;
}

Definedness would amount to testing the "storage" against the undefined
"value".

Determining denseness or sparseness could be done by
traits/concepts/whatever TMP supports, this is still an open question in my
mind.
Post by Vicente J. Botet Escriba
template <typename T, typename DegenerateValue>
struct optional <degenerate<T,DegenerateValue>>
{
// include here the degenerated specialization
}
In this way optional doesn't changes its interface (preserv only one
template parameter), and we are able to have
optional<T>
and
optional<degenerate<Player, integral_constant<Player, Player(-1)>>
I'd say yes, this is a fair idea. You take the word "degenerate" and make
it an adjective to the type Player, with a value representing
disengagedness. This would be the result of a refactoring the templates, I
think.

I think that your degenerate<Player, integral_constant<Player, Player(-1)>(using your definition of
degenerate) would benefit of being a full-fledged type representing Player
- Player(-1) and actually used in the signature of optional<> members.

This object would then build the "sparse" type Player - Player(-1) from the
"dense" type Player. The same space-efficient optimization of optional<>could be used for bool
and all other "sparse" types all the same.

This makes me think that Core C++ would benefit having a way of defining
truly "sparse" enums.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Nicol Bolas
2013-09-04 12:37:32 UTC
Permalink
On Tuesday, September 3, 2013 11:43:48 PM UTC-7, Vicente J. Botet Escriba
Post by Vicente J. Botet Escriba
What do you think about defining the degenerate class empty
template <typename T, typename DegenerateValue>
struct degenerate
{
typedef T value_type;
typedef DegenerateValue degenerate_value_type;
};
and use it to make specialization of optional
template <typename T, typename DegenerateValue>
struct optional <degenerate<T,DegenerateValue>>
{
// include here the degenerated specialization
}
In this way optional doesn't changes its interface (preserv only one
template parameter), and we are able to have
optional<T>
and
optional<degenerate<Player, integral_constant<Player, Player(-1)>>
That seems way too much like a kludge. Sure, you can `template using=` your
way around it, but we shouldn't *have* to do things like that just to make
something useable. What is so wrong with just making `optional<T, Tester>`
as a variation? We do something similar for `unique_ptr`.

I can't imagine how difficult it would be to implement that, with all those
`std::enable_if`s and everything. That's a huge burden compared to simply
having another template overload.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
David Krauss
2013-09-04 13:35:17 UTC
Permalink
Post by Nicol Bolas
That seems way too much like a kludge. Sure, you can `template using=`
your way around it, but we shouldn't *have* to do things like that just
to make something useable. What is so wrong with just making `optional<T,
Tester>` as a variation? We do something similar for `unique_ptr`.
unique_ptr uses a deleter, which is part of the smart pointer framework. (
shared_ptr also has them, albeit not as a template argument.) And they are
analogous to container allocators. Actually we would be better off if
unique_ptr had used an Allocator argument; it could be used by make_uniqueeven if
unique_ptr itself only needs the destructor/deallocator capability.

It's good to avoid adding new *kinds* of template arguments for unique
situations. My above suggestion is to name it excluded_value<…>, and reuse
the same concept for compile-time numeric ranges and such. So this use-case
would be naturally forming an optional over a type excluding a particular
value. The Tester concept could be reused for other things. No need to
tackle the world all at once, but at least there's a way forward to
genericity.
Post by Nicol Bolas
I can't imagine how difficult it would be to implement that, with all
those `std::enable_if`s and everything. That's a huge burden compared to
simply having another template overload.
Not sure what you mean. It would be a straightforward partial
specialization. Adding a second argument as you propose would also involve
partial specialization; the primary template we now know as optional would
become the Tester=void specialization. Aside from the role reversal over
which is primary and which is specialized, the implementations would be
exactly the same.

Note that the one-argument style can be already be implemented/prototyped
as a third-party library.

I think Tester might be a better semantic than DegenerateValue as it allows
support for NaN, which behaves badly with direct comparison. But whatever
is used, can probably be specialized on both, since a functor doesn't look
much like a compile-time constant.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
t***@gmail.com
2013-09-04 14:20:29 UTC
Permalink
David,

It's good to avoid adding new *kinds* of template arguments for unique
Post by David Krauss
situations. My above suggestion is to name it excluded_value<…>, and
reuse the same concept for compile-time numeric ranges and such. So this
use-case would be naturally forming an optional over a type excluding a
particular value.
Indeed but I would go much further than this. I would say that you can only
apply this space-saving technique if the type you declare has a "free
slot", i.e. the space of allowable values does not take up all the values
that can be stored by the storage allocated to the object. I came up with
the words "dense" and "sparse" for qualifying this dichotomy of types. The
choice of the implementation of "optional" for these types would only need :

- The concepts Sparse<T> and Dense<T>,
- The ability to get the underlying_type of a type T, whether Sparse or
Dense,
- For a type T that is Sparse, the ability to get the first value of the
underlying type not used by type T,
- Concepts-lite :)

Then, the ability to construct Sparse types, on the top of my head:

- A meta-function remove_value<type T, T v> that would build a type
based on T, that behaves like T, except that it cannot take value v. (The
resulting type would be sparse, as there would at least be one free value)
- Integer values that can limit their values to a sequence of disjoint
ranges : limited<int, range<int, -3, 10>, range<int, 20, 50>> (Disclaimer:
syntax subject to change)
- Something like "enum private class" that can create an enumerated
type, for which objects of that type can only take the values represented
by an enumerator.
- ...

I think Tester might be a better semantic than DegenerateValue as it allows
Post by David Krauss
support for NaN, which behaves badly with direct comparison. But whatever
is used, can probably be specialized on both, since a functor doesn't look
much like a compile-time constant.
Using the semantics of Sparse<T> and Dense<T> as outlined above, I'd say
Dense<double> and Dense<float>, although I don't know if there are any
values left free by IEEE754.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
David Krauss
2013-09-04 15:22:59 UTC
Permalink
Post by t***@gmail.com
David,
It's good to avoid adding new *kinds* of template arguments for unique
Post by David Krauss
situations. My above suggestion is to name it excluded_value<…>, and
reuse the same concept for compile-time numeric ranges and such. So this
use-case would be naturally forming an optional over a type excluding a
particular value.
Indeed but I would go much further than this. I would say that you can
only apply this space-saving technique if the type you declare has a "free
slot", i.e. the space of allowable values does not take up all the values
that can be stored by the storage allocated to the object. I came up with
the words "dense" and "sparse" for qualifying this dichotomy of types. The
Please no, dense and sparse have mathematical meanings completely different
from this. Rational numbers over a bignum type could sort-of be called
dense in the mathematical sense.
Post by t***@gmail.com
- The concepts Sparse<T> and Dense<T>,
What's the point of complementary concepts? Define "Dense" as "not
Sparse." And Sparse (or ValueConstraint) is just the conceptual interface
of excluded_value or degenerate or whatever.

Making Dense a specific concept implies that it has specific requirements
besides being not Sparse, which opens the possibility of a type that
satisfies neither. Then what would optional do? Implementation would also
be complicated, with a need for enable_if as Nicol mentioned.
Post by t***@gmail.com
- The ability to get the underlying_type of a type T, whether Sparse
or Dense,
Maybe I'm confused. What could the underlying type of a Dense type be,
besides itself?

Using the semantics of Sparse<T> and Dense<T> as outlined above, I'd say
Post by t***@gmail.com
Dense<double> and Dense<float>, although I don't know if there are any
values left free by IEEE754.
There are as many distinct NaNs as there are numbers between 0.5 and 1, but
it's a bad idea to try to distinguish them.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
t***@gmail.com
2013-09-04 16:04:05 UTC
Permalink
Post by David Krauss
Please no, dense and sparse have mathematical meanings completely
different from this. Rational numbers over a bignum type could sort-of be
called dense in the mathematical sense.
Yes, although I am not a mathematician, I know that mathematics have, to my
dismay, assigned precise meaning to a huge part of the dictionary. I just
found these better than OhNoItsPacked<T> and
YessssssThereIsACornerThereFitItInFitItInQuick<T> :-)

If you agree with the idea, would you mind help me find better names?
Post by David Krauss
Post by t***@gmail.com
- The concepts Sparse<T> and Dense<T>,
What's the point of complementary concepts? Define "Dense" as "not
Sparse."
Of course.

Implementation would also be complicated, with a need for enable_if as
Post by David Krauss
Nicol mentioned.
Or Concepts Lite. (N3701) Sparse<T> and !Sparse<T> should cover a great
deal of value semantics.

I'm getting ahead of myself, I know. I'm imagining what an ideal
implementation of optional<T> would look like.
Post by David Krauss
Post by t***@gmail.com
- The ability to get the underlying_type of a type T, whether Sparse
or Dense,
Maybe I'm confused. What could the underlying type of a Dense type be,
besides itself?
enums and enum classes are Dense, but their underlying types are different.

Using the semantics of Sparse<T> and Dense<T> as outlined above, I'd say
Post by David Krauss
Post by t***@gmail.com
Dense<double> and Dense<float>, although I don't know if there are any
values left free by IEEE754.
There are as many distinct NaNs as there are numbers between 0.5 and 1,
but it's a bad idea to try to distinguish them.
So can we create a C++ type that can store doubles that are non-NaN? What
would be the interface of such a type?
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
t***@gmail.com
2013-09-04 13:40:37 UTC
Permalink
Post by Nicol Bolas
We do something similar for `unique_ptr`.
Where exactly? If you think about the ability of specifying the deleter,
yes, the implementation technique you propose is applied. But no it's not
the same. The deleter will still be called ONLY if p.get() == nullptr. You
don't have the possibility to change the precondition to p.get() ==
0xDEADBEEF. The set of pointers for which the deleter can be called is the
same for both overloads.

I can't imagine how difficult it would be to implement that, with all those
Post by Nicol Bolas
`std::enable_if`s and everything. That's a huge burden compared to simply
having another template overload.
Indeed. And it would be a shame if standardizing such an overload NOW would
get in the way of an automated way of knowing whether the type you want to
optionalize has a "free slot" or not, that would otherwise be permitted by,
say concepts-lite, or strong typedefs.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Vicente J. Botet Escriba
2013-09-04 16:05:44 UTC
Permalink
Post by Nicol Bolas
On Tuesday, September 3, 2013 11:43:48 PM UTC-7, Vicente J. Botet
What do you think about defining the degenerate class empty
template <typename T, typename DegenerateValue>
struct degenerate
{
typedef T value_type;
typedef DegenerateValue degenerate_value_type;
};
and use it to make specialization of optional
template <typename T, typename DegenerateValue>
struct optional <degenerate<T,DegenerateValue>>
{
// include here the degenerated specialization
}
In this way optional doesn't changes its interface (preserv only
one template parameter), and we are able to have
optional<T>
and
optional<degenerate<Player, integral_constant<Player, Player(-1)>>
That seems way too much like a kludge. Sure, you can `template using=`
your way around it, but we shouldn't /have/ to do things like that
just to make something useable. What is so wrong with just making
`optional<T, Tester>` as a variation? We do something similar for
`unique_ptr`.
There is nothing wrong. Just this must be done quickly as once in C++14
it would be not so easy to change the std::optional interface. My
suggestion could be adopted just after c++14 is released.

Vicente
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
t***@gmail.com
2013-09-04 16:16:40 UTC
Permalink
Vicente,
Post by Vicente J. Botet Escriba
There is nothing wrong. Just this must be done quickly as once in C++14
it would be not so easy to change the std::optional interface. My
suggestion could be adopted just after c++14 is released.
Vicente
This must NOT be done quickly, as once in C++14 it would not be so easy to
take out, except by deprecation. If you wish to propose it to the
committee, please write the proposal and go for it. I think it's not ready
at all. (try feeding the template with a reference type, for instance)
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Ville Voutilainen
2013-09-04 16:24:44 UTC
Permalink
Post by t***@gmail.com
Vicente,
Post by Vicente J. Botet Escriba
There is nothing wrong. Just this must be done quickly as once in
C++14 it would be not so easy to change the std::optional interface. My
suggestion could be adopted just after c++14 is released.
Vicente
This must NOT be done quickly, as once in C++14 it would not be so easy to
take out, except by deprecation. If you wish to propose it to the
committee, please write the proposal and go for it. I think it's not ready
at all. (try feeding the template with a reference type, for instance)
Well, the time for such proposals for c++14 is well past, the NB ballot is
over and the NB comments are in
the pre-Chicago mailing, so whatever you come up with now is going to be
out of scope for c++14 unless
you convince the committee that it's an urgent bug fix, which I doubt will
happen.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Ville Voutilainen
2013-09-04 16:20:51 UTC
Permalink
On 4 September 2013 19:05, Vicente J. Botet Escriba <
Post by Vicente J. Botet Escriba
In this way optional doesn't changes its interface (preserv only one
template parameter), and we are able to have
Post by Maurice Bos
optional<T>
and
optional<degenerate<Player, integral_constant<Player, Player(-1)>>
That seems way too much like a kludge. Sure, you can `template using=`
your way around it, but we shouldn't *have* to do things like that just
to make something useable. What is so wrong with just making `optional<T,
Tester>` as a variation? We do something similar for `unique_ptr`.
There is nothing wrong. Just this must be done quickly as once in C++14
it would be not so easy to change the std::optional interface. My
suggestion could be adopted just after c++14 is released.
Perhaps we should leave optional well alone and consider something like
http://rk.hekko.pl/constrained_value/
as a potential solution for cases where you want a type is a constrained
value rather than a nullable
proxy.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
t***@gmail.com
2013-09-04 17:57:48 UTC
Permalink
Ville,
Post by Ville Voutilainen
Perhaps we should leave optional well alone and consider something like
http://rk.hekko.pl/constrained_value/
as a potential solution for cases where you want a type is a constrained
value rather than a nullable
proxy.
I've been OK for leaving optional<> alone from the beginning of this
thread. Tell me. Why I you interesting specifically in considering
constrained values types? (Possibly in another thread)
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Ville Voutilainen
2013-09-04 18:19:29 UTC
Permalink
Post by t***@gmail.com
Post by Ville Voutilainen
Perhaps we should leave optional well alone and consider something like
http://rk.hekko.pl/**constrained_value/<http://rk.hekko.pl/constrained_value/>
as a potential solution for cases where you want a type is a constrained
value rather than a nullable
proxy.
I've been OK for leaving optional<> alone from the beginning of this
thread. Tell me. Why I you interesting specifically in considering
constrained values types? (Possibly in another thread)
It seems to me that constrained value types would solve the problem you
describe, whereas optional
is designed to solve a quite different problem.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
t***@gmail.com
2013-09-04 19:02:09 UTC
Permalink
Ville,

Sorry, I don't follow you. Which problem exactly is would constrained
values solve and how? I have described at least half a dozen problems in
this thread.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Ville Voutilainen
2013-09-04 19:26:59 UTC
Permalink
Post by t***@gmail.com
Ville,
Sorry, I don't follow you. Which problem exactly is would constrained
values solve and how? I have described at least half a dozen problems in
this thread.
The one you originally requested feedback for. The possible values for a
player are a subset
of the values for a square.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
t***@gmail.com
2013-09-04 21:47:32 UTC
Permalink
Ville,
Post by Ville Voutilainen
The one you originally requested feedback for. The possible values for a
player are a subset
of the values for a square.
I see. Yet, going the other way around is more natural. optional<> lets me
reuse the board with any set of tokens, because it has the right semantics.
A board is "9 squares that MAY include a token".

enum class Token { CROSS, NOUGHT };

vector<optional<Token>> board(9);

The above is more natural for me to write than:

enum class SquareState { EMPTY, CROSS, NOUGHT };

vector<SquareState> board(9);
using token_t = bounded_enum<SquareState, SquareState::CROSS, SquareState::
NOUGHT>;

If I use the latter form, the rest of the code is littered with tests like board[i]
!= SquareState::EMPTY. With the former, these cases amount to a
bool(board[i]).

With the former, a variable of type Token is either Token::CROSS or
Token::NOUGHT, provided that I preclude myself from explicitly constructing
a Token from an int. A variable accommodating for emptiness is an
optional<Token>.

More, I can write this:

template<typename TokenType, size_t nbRows, size_t nbCols>
struct Board {
using square_t = optional<TokenType>;
vector<square_t> squares = vector<square_t>(nbRows * nbCols);
};

And emptiness is a concept that is implemented in the board, not in the
token type. Emptiness is then treated uniformly across all games that will
use the struct Board.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Nevin Liber
2013-09-04 22:02:13 UTC
Permalink
Post by t***@gmail.com
And emptiness is a concept that is implemented in the board, not in the
token type. Emptiness is then treated uniformly across all games that will
use the struct Board.
Is there *any *usage experience with this? If not, why not? Unlike
optional (which has to worry about object lifetime), it isn't like this is
particularly difficult to implement, even in C++03.

I'm not seeing a need to standardize such a thing.
--
Nevin ":-)" Liber <mailto:***@eviloverlord.com> (847) 691-1404
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
t***@gmail.com
2013-09-04 22:15:07 UTC
Permalink
Post by Nevin Liber
Post by t***@gmail.com
And emptiness is a concept that is implemented in the board, not in the
token type. Emptiness is then treated uniformly across all games that will
use the struct Board.
Is there *any *usage experience with this? If not, why not? Unlike
optional (which has to worry about object lifetime), it isn't like this is
particularly difficult to implement, even in C++03.
Are you talking about the struct Board, or the degenerate<> template? Both
have usage experience, although I can testify only of mine. I was only
proposing the degenerate<> template.

I'm not seeing a need to standardize such a thing.
What "such a thing"? I'm sorry, with all that has been said in this thread,
I can't know clearly what "thing" you're referring to.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Nevin Liber
2013-09-04 22:24:20 UTC
Permalink
Post by t***@gmail.com
Are you talking about the struct Board, or the degenerate<> template?
C'mon. Did you really think I meant struct Board??

Fine. I see no need to standardize either Board or degenerate. The use
case is fairly minor.
--
Nevin ":-)" Liber <mailto:***@eviloverlord.com> (847) 691-1404
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
David Krauss
2013-09-05 01:15:31 UTC
Permalink
Post by Nevin Liber
Fine. I see no need to standardize either Board or degenerate. The use
case is fairly minor.
As I mentioned, the constrained type template and the user-defined
specialization of std::optional thereupon can both be provided as a
user-defined library. Boost would probably be very receptive, and promptly
get it tested by real users.

You don't need concepts for the implementation; as mentioned you don't even
need C++11. It should be easy to upgrade to concepts, but initial
implementation without would probably be easier.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
David Krauss
2013-09-05 01:17:05 UTC
Permalink
Post by David Krauss
the constrained type template
sorry, "constrained value template". And it would probably be a good idea
to look for such a library in preexisting form.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Tony V E
2013-09-05 21:03:05 UTC
Permalink
Just some comments on the entire thread:

- the "squeeze every last ounce" part of me likes the idea of an optional
that steals an unused value as the "disengaged" state (and I've toyed with
the idea in the past).
We do this all the time at hock with ints - returning -1 or 0, etc when we
want to return "can't give you a real value". So instead of a #define or
const int BAD_FOO_VALUE, using the language of optional<> might make some
sense. Or a separate class that has the same/similar interface as
optional. (Look at the upcoming Boost.Expected as another
similar-to-but-not-exacly-optional class).

- your original problem might just as well be solved by deriving enums from
enums, or an enum constructor:

enum token { CROSS, NOUGHT };

enum SquareState
{
CROSS, NOUGHT, EMPTY;

SquareState(token t)
{
* this = t;
}
};

I'm not saying we can derive enums from enums or give them constructors, but
- it has been mentioned more than once in the past
- you could make your own enum-like classes that had constructors and/or
derivation

Tony
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Bjorn Reese
2013-09-03 12:51:30 UTC
Permalink
Post by t***@gmail.com
that it should be a "drop-in replacement" for it. degenerate<T,v>
actually implements an equivalent of std::optional<T - {v}>, in the case
of when you're not using all the values of a type.
Have you consider an extension where there are several "none" values?

For instance, you may have a simple lexer/tokenizer that returns the
next token from the input. Let us say it recognizes integer,
floating-point numbers, and string. In addition to this, the tokenizer
may return special tokens to indicate errors or end-of-file.

enum class Token { ERROR, EOF, INTEGER, FLOAT, STRING };

So here we have two values that are set apart from the encodable set.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
t***@gmail.com
2013-09-03 14:28:01 UTC
Permalink
Bjorn,
Post by Bjorn Reese
Have you consider an extension where there are several "none" values?
Yes. There are several problems. Let's call V the set of values of a type T
:

Choose a value v1 within V as the "none" value. The type degenerate<T, v1>would be usable as a replacement for a hypothetical optional<T
- {v1}>. Now choose a value v2 within V :

- if it's the same as v1, then degenerate<degenerate<T, v1>, v2> would
degrade to degenerate<T, v1>.
- if it's different of v1, then degenerate<degenerate<T, v1>, v2> would
be usable as a replacement for a hypothetical optional<optional<T -
{v1,v2}>>.

You can see that the count set of values is decrementing at every step.
When you reach the empty set, what should happen? We could leave this to
the programmer.
To answer more precisely, it would suffice to accomodate class type with
value semantics as type T. I think this can be done with functors.

For instance, you may have a simple lexer/tokenizer that returns the
Post by Bjorn Reese
next token from the input. Let us say it recognizes integer,
floating-point numbers, and string. In addition to this, the tokenizer
may return special tokens to indicate errors or end-of-file.
enum class Token { ERROR, EOF, INTEGER, FLOAT, STRING };
So here we have two values that are set apart from the encodable set.
In what order ? The most logical to me would be :

enum class Token { ERROR, EOF, INTEGER, FLOAT, STRING };
using possibly_erroneous_result_t = degenerate<Token, Token::ERROR>;
using result_t = degenerate<possibly_erroneous_result_t, constexpr []() ->possibly_erroneous_result_t
{ return possibly_erroneous_result_t{ Token::EOF }; }>;

I did not yet try to write the variant accomodating classes with functors.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Loading...