Actually organizing all of the symbols within a file is pretty much a
losing game in general, but I was trying to put the is* traits together
and accidentally put some in the section containing Unconst, Unshared,
and Unshared. So, this just moves Unconst and Unshared so that they're
next to Unqual again.
There is zero code that's actually changed. It's just moved within the
file.
isNumeric!T is equivalent to isInteger!T || isFloatingPoint!T, so I
suppose that its value is somewhat questionable with how simple it is,
but it does save a template instantiation in the process and arguably is
clearer for documentation purposes.
There is an isNumeric in std.traits, but I would have to study it in
detail to figure out exactly what it accepts, because it is written in a
rather confusing manner (though it does clearly accept enums, which this
isNumeric does not).
I have tried to make lib.sys.traits' isNumeric very clear in its
documentation about what it accepts.
isPointer does exactly what it says on the tin, so it's quite
straightforward all around.
I also beefed up the documentation on isDynamicArray to discuss a bit
about dynamic arrays vs slices and why the kind of memory that backs a
dynamic array is irrelevant to the type (and thus irrelevant to the
trait) - as well as pointing out that they're completely different from
pointers, if that isn't obvious enough.
isInteger is similar to std.traits.isIntegral, but I went with a
different name because it's specifically testing for the built-in
integer types and not for all types which are even vaguely integral. It
also helps distinguish from __traits(isIntegral, T) which accepts just
about anything and its grandmother which is even vaguely integer-like /
integral (to the point that I honestly question how much sense it makes
to ever use it).
The main difference between isInteger and std.traits.isIntegral is that
isIntegral accepts enums, whereas isInteger does not. isIntegral also
theoretically accepts cent and ucent, but since it's now an error to use
those, I don't think that it accepts them any longer in practice anyway.
isInteger makes no attempt to accept them and does not accept their
replacement types, because they're not built-in (and it's not clear to
me that we've fully sorted out what we're doing with those anyway).
isFloatingPoint is similar to std.traits' isFloatingPoint except that
the std.traits version accepts everything that __traits(isFloating, T)
does along with any type that implicitly converts to real. The
lib.sys.traits version accepts float, double, and real only.
This ports over isSomeString and isSomeChar from std.traits.
isSomeString behaves the same (since the std.traits version doesn't
accept enums), but the new isSomeChar is different in that the old one
accepts enums, and this one rejects them. The new isSomeChar also has a
simplified implementation, but the behavior is the same aside from how
enums are treated.
The documentation and tests have been beefed up in the process, and I
added a bit more to the documentation / tests for isDynamicArray and
isStaticArray based on what came up with the tests for isSomeString and
isSomeChar.
It is not my plan to port over any of the other traits in std.traits
which are specifically for strings (e.g. isNarrowString), so checking
string types with lib.sys.traits should be less confusing than it has
been with std.traits.
Their behavior does differ slightly from that of their std.traits
counterparts, because they are false for enums even when an enum's base
type would match. std.traits is inconsistent with how it treats enums
(e.g. isSomeString is false for them, because it was causing bugs with
ranges for them to pass, but many of the other traits are true for
enums), but unfortunately, this isn't something that we've been able to
fix in std.traits (std.traits' isDynamicArray even mentions the problem
in a comment in its documentation), since it would break exising code
(in potentially very subtle ways no less).
I added a section to the module documentation explaining the reasoning
for how lib.sys.traits handles enums and implicit conversions with the
isXXX and hasXXX traits, which will hopefully not only make the
situation clear but also help educate some folks on the issues with
implicit conversions and template constraints.
I also beefed up the tests and documentation for isDynamicArray and
isStaticArray.
These do the same as their std.traits counterparts, but I've beefed up
the documentation and examples a bit.
I'm not sure that it's worth adding SharedConstOf, SharedInoutOf, or
SharedConstInoutOf as well, since that seems kind of excessive to me -
though it looks like std.variant does use SharedConstOf in its tests -
but in either case, I think that those can be left for a later commit if
we want to keep them, since this is already adding four symbols.
This starts lib.sys.traits with just Unconst, Unshared, and Unqual. Both
the tests and documentation have been beefed up from what std.traits
has, with many more examples provided to try to make the exact semantics
crystal clear, whereas the previous explanation in the docs was pretty
sparse. The actual semantics are the same though.
This isn't what I would have chosen for traits first, but they're used
heavily in unit tests, so they got moved up in the list.
I used version (StdDdoc) for the ddoc version, since that's what we do
normally in Phobos. I don't know if we'll want or need to change that
later given than Phobos v3 technically isn't Std, but all that really
matters there is that it's a specific version identifier that we use for
building Phobos' documentation and which no other projects would use.
And since Phobos v3 currently is set up to build with dub rather than
Phobos' normal build system, I expect that the documentation generation
is completely busted right now anyway. So, that'll need to get fixed at
some point.