Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

"Info" as a name

76 views
Skip to first unread message

Alf P. Steinbach

unread,
May 22, 2017, 3:45:10 PM5/22/17
to
It's said [by whom? hey needs reference] that “Info” as a name indicates
a design problem.

Consider the following code of mine:

class Encoding_value
{
private:
struct Info
{
int n_leading_ones;
Byte bits;

Info( Byte const code )
: n_leading_ones{ 0 }
, bits{ code }
{
for( Byte mask = 0b1000'0000; bits & mask; mask >>= 1 )
{
bits &= static_cast<Byte>( ~mask );
++n_leading_ones;
}
}
};

public:
Info const info;

Encoding_value( Byte const code )
: info{ code }
{}
};

It would be nice, probably, to find some way to express it without the
non-descriptive “Info” as a name, yet without ugly accessors or code
verbosity or other negative properties.

One possible design problem, that “Info” maybe indicates, is that the
`private` declaration of the “Info” class is partially public in the
sense that client code is intended to use its member names, like

auto const& value = Encoding_value{
static_cast<Byte>( *in_pos++ )
}.info;

// E.g. access `value.n_leading_ones` here.

Oh well that usage has its own problems, it's a darkish little corner
case of the language. Hm. Comments?


Cheers!,

- Alf

Richard

unread,
May 22, 2017, 7:11:48 PM5/22/17
to
[Please do not mail me a copy of your followup]

"Alf P. Steinbach" <alf.p.stein...@gmail.com> spake the secret code
<ofvete$dl6$1...@dont-email.me> thusly:

>It's said [by whom? hey needs reference] that "Info" as a name indicates
>a design problem.

Additional candidates:

"Data"
"Manager"
"Helper"
"Utility"

Although Manager sometimes isn't abused.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Terminals Wiki <http://terminals-wiki.org>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>

Ian Collins

unread,
May 22, 2017, 10:37:03 PM5/22/17
to
On 05/23/17 11:11 AM, Richard wrote:
> [Please do not mail me a copy of your followup]
>
> "Alf P. Steinbach" <alf.p.stein...@gmail.com> spake the secret code
> <ofvete$dl6$1...@dont-email.me> thusly:
>
>> It's said [by whom? hey needs reference] that "Info" as a name indicates
>> a design problem.
>
> Additional candidates:
>
> "Data"
> "Manager"
> "Helper"
> "Utility"
>
> Although Manager sometimes isn't abused.

Although managers are :)

--
Ian

Alf P. Steinbach

unread,
May 23, 2017, 12:41:09 AM5/23/17
to
On 23-May-17 1:39 AM, Stefan Ram wrote:
> "Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:
>> It's said [by whom? hey needs reference] that "Info" as a
>> name indicates a design problem.
>
> The name "info" is maximally vague, because anything can be
> "information". It is appropriate if exactly this /is/ the
> meaning of an object of this class: to carry a piece of
> information of which no further details are known.
>
> I don't see why the private struct has to be there at all.

To support the `const` (immutability) of the data members.


> Why not just
>
> class encoding_value
> { int leading_one_count_; Byte value;
> public:
> int leading_one_count() const
> { return this->leading_one_count; }
>
> encoding_value( Byte const value ):
> leading_one_count_{ 0 }, value{ value }
> { for( Byte mask = 0b1000'0000; bits & mask; mask >>= 1 )
> { bits &= static_cast<Byte>( ~mask ); ++leading_one_count_; }}}

Well, I want `const` data members instead of accessors.

The `const` members give more clean usage, they ensure initialization,
they ensure that all are available (e.g. the above code lacks a `value`
accessor), they're more concise code, their specification has less
redundancy, and they don't leave one with a permanent cognitive
dissonance attack on one's feeling that accessors are µ-inefficient.


> ...
>
> auto const & value = encoding_value{ static_cast< Byte >( *in_pos++ ) };
>
> ? The above code is untested, but one should get the general
> idea. (Books like »Hacker's Delight« might contain optimized
> code to count the number of leading ones for integral values
> with a known number of bits.)
>
> Another possible refactor might be:
>
> class encoding_value
> { Byte value; int leading_one_count;
> public:
> int leading_one_count() const
> { return this->leading_one_count; }
>
> encoding_value( Byte const value ): value{ value },
> leading_one_count{ ::bit_utils::leading_one_count_of( value )} {}}
>
> , where »::bit_utils« might be a namespace or a class.
>
> To count the leading ones of an integer is a general
> utility function that should not be re-implemented in
> every class that needs it.

Well, this one would be more complicated because it introduces a
dependency for something trivial, and thereby adds code (omitted in this
example) to clear the upper bits of the `value`.

I think my ideal refactor is to just swap what's `class` and what's
`struct`, and add a type conversion responsibility to the outer class:

struct Encoding_value
{
class Info
{
public:
int n_leading_ones;
Byte bits;

private:
friend class Encoding_value;

Info( Byte const code )
: n_leading_ones{ 0 }
, bits{ code }
{
for( Byte mask = 0b1000'0000; bits & mask; mask >>= 1 )
{
bits &= static_cast<Byte>( ~mask );
++n_leading_ones;
}
}
};

Info const info;

Encoding_value( char const code )
: info{ static_cast<Byte>( code ) }
{}
};

But there's still the issue of the naming, “Info”…

Cheers!,

- Alf

Alf P. Steinbach

unread,
May 23, 2017, 12:42:58 AM5/23/17
to
Oh, I'm stupid. “M” for the class and “m” for the data member. Of course. :)

Cheers!,

- Alf

Alf P. Steinbach

unread,
May 23, 2017, 1:57:57 AM5/23/17
to
On 22-May-17 9:44 PM, Alf P. Steinbach wrote:
> It's said [by whom? hey needs reference] that “Info” as a name indicates
> a design problem.
>
> Consider the following code of mine:
>
> class Encoding_value
> {
> private:
> struct Info
> {
> int n_leading_ones;
> Byte bits;
>
> Info( Byte const code )
> : n_leading_ones{ 0 }
> , bits{ code }
> {
> for( Byte mask = 0b1000'0000; bits & mask; mask >>= 1 )
> {
> bits &= static_cast<Byte>( ~mask );
> ++n_leading_ones;
> }
> }
> };
>
> public:
> Info const info;
>
> Encoding_value( Byte const code )
> : info{ code }
> {}
> };
>
> It would be nice, probably, to find some way to express it without the
> non-descriptive “Info” as a name, yet without ugly accessors or code
> verbosity or other negative properties.

My own solution:

struct Encoding_value
{
class Members
{
public:
int n_leading_ones;
Byte bits;

private:
friend class Encoding_value;

Members( Byte const code )
: n_leading_ones{ 0 }
, bits{ code }
{
for( Byte mask = 0b1000'0000; bits & mask; mask >>= 1 )
{
bits &= static_cast<Byte>( ~mask );
++n_leading_ones;
}
}
};

Members const m;

Encoding_value( char const code )
: m{ static_cast<Byte>( code ) }
{}
};

Cheers!,

- Alf


Scott Lurndal

unread,
May 23, 2017, 8:41:00 AM5/23/17
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:
>It's said [by whom? hey needs reference] that “Info” as a name indicates
>a design problem.
>
>Consider the following code of mine:
>
> class Encoding_value
> {
> private:
> struct Info
> {
> int n_leading_ones;
> Byte bits;
>
> Info( Byte const code )
> : n_leading_ones{ 0 }
> , bits{ code }
> {
> for( Byte mask = 0b1000'0000; bits & mask; mask >>= 1 )
> {
> bits &= static_cast<Byte>( ~mask );
> ++n_leading_ones;

If one is working with a standard twos-complement modern
processor and happens to have g++ available:

#include <limits.h>
#include <stddef.h>
#include <stdio.h>

template <class T> size_t
count_leading_ones(T bits)
{
return (!bits)?0:__builtin_clzll(~bits) - (sizeof(unsigned long long)-sizeof(T)) * CHAR_BIT;
}

int main(int argc, const char **argv, const char **envp)
{

printf("%u\n", count_leading_ones((char)0xf0));
}

Alain Ketterlin

unread,
May 23, 2017, 9:49:26 AM5/23/17
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:

> Consider the following code of mine:
>
> class Encoding_value
> {
> private:
> struct Info
> {
> int n_leading_ones;
> Byte bits;
>
> Info( Byte const code )
> : n_leading_ones{ 0 }
> , bits{ code }
> {
> for( Byte mask = 0b1000'0000; bits & mask; mask >>= 1 )
> {
> bits &= static_cast<Byte>( ~mask );
> ++n_leading_ones;
> }
> }
> };
>
> public:
> Info const info;
>
> Encoding_value( Byte const code )
> : info{ code }
> {}
> };

Why not just a named constructor?

class Encoding_value
{
public:
int const n_leading_ones;
Byte const bits;
private:
Encoding_value (int n, Byte b) : n_leading_ones(n),bits(b) {}
public:
Encoding_value make(Byte code)
{
...
return Encoding_value(...);
}
};

plus another ctor wrapping/using make() and the copy-ctor if you really
want to construct from Byte directly.

-- Alain.

Alf P. Steinbach

unread,
May 23, 2017, 10:31:58 AM5/23/17
to
The FAQ uses your terminology, but I call a “named constructor” a
“factory function”. Not the first time I disagree with Marshall Cline. I
think of “named constructor” as an actual constructor with a name,
usually provided by a tag argument.

I wasn't thinking in the direction of factories, and I don't know why.

Then it looks like this:

class Encoding_value
{
private:
Encoding_value( int const n, Byte const b )
: n_leading_ones( n ), bits( b )
{}

public:
int const n_leading_ones;
Byte const bits;

static auto from( char const code )
-> Encoding_value
{
auto bits = static_cast<Byte>( code );
int n_leading_ones = 0;

for( Byte mask = 0b1000'0000; bits & mask; mask >>= 1 )
{
bits &= static_cast<Byte>( ~mask );
++n_leading_ones;
}
return {n_leading_ones, bits};
};
};

Cheers!, & thanks,

- Alf

Tim Rentsch

unread,
May 24, 2017, 4:49:50 PM5/24/17
to
> The FAQ uses your terminology, but I call a ?named constructor? a
> ?factory function?. Not the first time I disagree with Marshall
> Cline. I think of ?named constructor? as an actual constructor with a
> name, usually provided by a tag argument.
>
> I wasn't thinking in the direction of factories, and I don't know why.
>
> Then it looks like this:
>
> class Encoding_value
> {
> private:
> Encoding_value( int const n, Byte const b )
> : n_leading_ones( n ), bits( b )
> {}
>
> public:
> int const n_leading_ones;
> Byte const bits;
>
> static auto from( char const code )
> -> Encoding_value
> {
> auto bits = static_cast<Byte>( code );
> int n_leading_ones = 0;
>
> for( Byte mask = 0b1000'0000; bits & mask; mask >>= 1 )
> {
> bits &= static_cast<Byte>( ~mask );
> ++n_leading_ones;
> }
> return {n_leading_ones, bits};
> };
> };

Rather than using a factory function, I think it's better to use
a delegating constructor. The following compiles (disclaimer:
untested) in C++11 and C++14 (and for my own amusement I wrote
the code so everything could be 'constexpr'):


typedef unsigned char Byte;

struct EncodedByte {
int const n_leading_ones;
Byte const low_bits;

constexpr
EncodedByte( char c )
: EncodedByte( high_ones( 0, c, 0x80 ), c )
{}

private:
constexpr
EncodedByte( int upper_ones, unsigned bits )
: n_leading_ones( upper_ones )
, low_bits( bits ^ -1u << 8-upper_ones )
{}

static constexpr int
high_ones( int n, unsigned bits, unsigned mask ){
return bits & mask ? high_ones( n+1, bits, mask>>1 ) : n;
}
};

Alf P. Steinbach

unread,
May 24, 2017, 5:21:12 PM5/24/17
to
Thanks, it's interesting how many natural ways there are to write this
particular miniature class.

Your version is less obvious to me, what with the recursion and general
indirection, even though I've read a few Lisp and functional programming
books (I think “Artifical Intelligence” by Patrick Henry Winston, with
its companion example book by Berthold whatever it was and him, was
Really Good™ as an introduction to Lisp, but now they're old books). But
the main thing that makes me unsure about this code is not the general
approach (very nice, modulo one's familiarity with idioms of functional
programming like in Lisp), it's the precedence of the C++ `^`, `<<` and
infix `-` operators. I'd had to look that up to be sure!

Someone™ should really write up a blog article about these approaches. :)

Tim Rentsch

unread,
May 25, 2017, 9:51:48 AM5/25/17
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:

> On 24-May-17 10:49 PM, Tim Rentsch wrote:
>> "Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:
>>
>>> [...how a class with const members might be written...]
> programming books [...].

Delegating constructors fit nicely into a functional/recursive
approach, but they have an important limitation in that they must
be unconditional. That means we must have a helper function in
some cases to calculate values for the member variables. Here
the code is taking advantage of the property that once the number
of leading ones is known, it's easy to mask off the leading ones
from the original parameter value, using a simple expression.
That leaves the recursive function high_ones(). I think it's
pretty easy to read functions like this once you get the hang of
it. Case in point: if we see this

return bits & mask ? high_ones( n+1, bits, mask>>1 ) : n;

we might think of it as a loop

while ( bits & mask ) {
n += 1, mask >>= 1;
}
return n;

which may help bridge the gap until the recursive writing becomes
more natural.

One advantage of adopting a functional/recursive approach is that
it provides a way of "looping" in functions but which still can
be 'constexpr'.


> But the main thing that makes me unsure about this code is not the
> general approach (very nice, modulo one's familiarity with idioms of
> functional programming like in Lisp), it's the precedence of the C++
> `^`, `<<` and infix `-` operators. I'd had to look that up to be
> sure!

Here's an easy way to remember (for operators that are also in C,
which holds in this case). Binary operators all group in ways
that are natural and sensible given what they do, except for the
bitwise operators. Bitwise operators form a "sandwich" around
the relational/equality operators: shift operators have higher
precedence, and bitwise-logic operators have lower precedence.
The two kinds of shift have the same precedence; the three kinds
of bitwise-logic operations each have their own level: & and |
parallel && and ||, and ^ is inbetween. The logic operations
having lower precedence than relational/equality operators is a
historical artefact of early C not having && and ||, so it made
sense (then, not now) for & and | to be "lower" than ==, etc,
for boolean combination of conditions.

Here is a variation on the previous class definition - the helper
function calculates both member values at once, packing them into
a single 'unsigned long long' value for subsequent extraction.
Notice the private constructor needs to be 'explicit' in this
case, to avoid an overloading ambiguity.

typedef unsigned char Byte;

struct EncodedByte {
int const n_leading_ones;
Byte const low_bits;

constexpr
EncodedByte( char c )
: EncodedByte( two_parts( 0, c & 255u, 0x80 ) )
{}

private:
typedef unsigned Bits;
typedef unsigned long long ULL;

explicit constexpr
EncodedByte( ULL upper_lower )
: n_leading_ones( upper_lower >> 8 )
, low_bits( upper_lower & 255 )
{}

static constexpr ULL
two_parts( ULL n, Bits b, Bits m ){
return b & m ? two_parts( n+1, b^m, m>>1 ) : n<<8 | b;
}
};

Alf P. Steinbach

unread,
May 25, 2017, 10:30:17 AM5/25/17
to
On 25-May-17 3:51 PM, Tim Rentsch wrote:
>>
> One advantage of adopting a functional/recursive approach is that
> it provides a way of "looping" in functions but which still can
> be 'constexpr'.

Oh, this was relaxed in C++14.

E.g. the following code compiles fine with g++ (but Visual C++ 2017
lacks support):

constexpr
auto sum_to( int const n )
-> int
{
int sum = 0;
for( int i = 1; i <= n; ++i ) { sum += i; }
return sum;
}

#include <stdlib.h>
auto main()
-> int
{
constexpr int n = 42;
char const a[sum_to( n )]{};
return (sizeof(a) == n*(n + 1)/2? EXIT_SUCCESS : EXIT_FAILURE);
}

Thanks for your comments, third approach for the problem, and further
help about how to remember operator precedence (snipped here). :)


Cheers!,

- Alf

Tim Rentsch

unread,
May 30, 2017, 9:53:38 AM5/30/17
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:

> On 25-May-17 3:51 PM, Tim Rentsch wrote:
>
>> One advantage of adopting a functional/recursive approach is that
>> it provides a way of "looping" in functions but which still can
>> be 'constexpr'.
>
> Oh, this was relaxed in C++14. [...]

Yes I know. I deliberately aim for C++11 compatibility where
I can, not just with constexpr but generally. Also I find a
functional/recursive style is in many cases easier to write
and easier to understand, so I am predisposed to use it in
cases where it isn't really awkward.

Alf P. Steinbach

unread,
May 30, 2017, 10:29:06 AM5/30/17
to
OK. I'm sorry but what I wrote

> (but Visual C++ 2017 lacks support)

was just wrong.

As I understand it Visual C++ 2017 eats `constexpr` functions with loops
for breakfast, enthusiastically. It also has modules, C++ 2017 file
system, and possibly coroutine support.

But I still haven't managed to fix my config script to use Visual C++
2017 instead of Visual C++ 2015 in the command line.

And for some inexplicable reason I keep forgetting that I haven't fixed it.


Cheers!,

- Alf

Ian Collins

unread,
May 30, 2017, 4:05:26 PM5/30/17
to
On 05/31/17 01:53 AM, Tim Rentsch wrote:
> "Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:
>
>> On 25-May-17 3:51 PM, Tim Rentsch wrote:
>>
>>> One advantage of adopting a functional/recursive approach is that
>>> it provides a way of "looping" in functions but which still can
>>> be 'constexpr'.
>>
>> Oh, this was relaxed in C++14. [...]
>
> Yes I know. I deliberately aim for C++11 compatibility where
> I can, not just with constexpr but generally.

C++14 is a better target these days, the improvements, while small, were
significant and every compiler I know supports it.

--
Ian
0 new messages