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

Polymorphic value type with limited size

23 views
Skip to first unread message

Marcel Mueller

unread,
Feb 27, 2016, 1:55:54 PM2/27/16
to
Let's have a dispatch table of an assembler.


/// Entry of the OP code lookup table, POD, compatible with binary_search().
/// @tparam L maximum length of Name.
template <size_t L>
struct opEntry
{ char Name[L]; ///< OP code name
void (Parser::*Func)(int);///< Parser function to call, receives the
agrument below.
int Arg; ///< Arbitrary argument to the parser function.
};

/// Map with opcode tokens, must be ordered.
const Parser::opEntry<8> Parser::opcodeMap[] =
{ {"add", &Parser::assembleADD, Inst::A_ADD }
, {"and", &Parser::assembleADD, Inst::A_AND }
, {"asr", &Parser::assembleADD, Inst::A_ASR }
, {"bpkt", &Parser::assembleSIG, Inst::S_BREAK }
, {"bra", &Parser::assembleBRANCH, false }
//...
};


Each parser function takes an (optional) argument. The type is fixed to
int in this example. But this is not safe. Mostly the type is some enum.
But each function takes another argument type, and of course, the third
argument in the table must match the argument type of the function.

For type safe operation it would be nice to have a type like


template <size_t L, typename P>
struct opEntry
{ char Name[L]; ///< OP code name
void (Parser::*Func)(P);///< Parser function to call, receives the
agrument below.
P Arg; ///< Arbitrary argument to the parser function.
};


But this does not work since each line of Parser::opcodeMap[] has
another type P in general and all of these structures must fit into the
same storage. Of course, there is only a solution when sizeof(P) has an
upper limit, since this is similar to a union.

Could this be expressed type safe in C++11, without the need to deal
with pointers, new and delete for each line?


Marcel

Öö Tiib

unread,
Feb 27, 2016, 3:49:11 PM2/27/16
to
On Saturday, 27 February 2016 20:55:54 UTC+2, Marcel Mueller wrote:
> Let's have a dispatch table of an assembler.
>
>
> /// Entry of the OP code lookup table, POD, compatible with binary_search().
> /// @tparam L maximum length of Name.
> template <size_t L>
> struct opEntry
> { char Name[L]; ///< OP code name
> void (Parser::*Func)(int);///< Parser function to call, receives the
> agrument below.
> int Arg; ///< Arbitrary argument to the parser function.
> };

I see you use string literals to initialize 'Name' so I don't
understand why there is that mutable array and that L, do you want
to dynamically modify it? String literal is guaranteed to live
forever so you could use pointer to it or pointer and length pair.

>
> /// Map with opcode tokens, must be ordered.
> const Parser::opEntry<8> Parser::opcodeMap[] =
> { {"add", &Parser::assembleADD, Inst::A_ADD }
> , {"and", &Parser::assembleADD, Inst::A_AND }
> , {"asr", &Parser::assembleADD, Inst::A_ASR }
> , {"bpkt", &Parser::assembleSIG, Inst::S_BREAK }
> , {"bra", &Parser::assembleBRANCH, false }
> //...
> };
>
>
> Each parser function takes an (optional) argument. The type is fixed to
> int in this example. But this is not safe. Mostly the type is some enum.
> But each function takes another argument type, and of course, the third
> argument in the table must match the argument type of the function.

Use some type-safe variant then instead of 'int' for example Boost.Variant,
QVariant, Eggs.Variant.

>
> For type safe operation it would be nice to have a type like
>
>
> template <size_t L, typename P>
> struct opEntry
> { char Name[L]; ///< OP code name
> void (Parser::*Func)(P);///< Parser function to call, receives the
> agrument below.
> P Arg; ///< Arbitrary argument to the parser function.
> };

That would not be issue if P was a variant not template argument.
'boost::variant<paramtype1,paramtype2,paramtype3>'. You must know
anyway all the different types of parameters when you initialize
that 'opcodeMap' of yours. Btw words like "Map" and "List" may be
misleading in name of variable since readers sometimes assume
underlying container.

>
> But this does not work since each line of Parser::opcodeMap[] has
> another type P in general and all of these structures must fit into the
> same storage. Of course, there is only a solution when sizeof(P) has an
> upper limit, since this is similar to a union.

No ... union is not type-safe, it is type-punning.

>
> Could this be expressed type safe in C++11, without the need to deal
> with pointers, new and delete for each line?

Unfortunately the 'variant' is not in C++11. The proposal of it
N4542 added default empty state to it while 'boost::variant' has
never-empty guarantee (unless you put 'boost::blank' as first type
that is). Huge language lawyer shitstorm and lot of controversy
later it has transformed into P0088R1 so maybe in C++17 we will have
it.

Alf P. Steinbach

unread,
Feb 27, 2016, 3:58:37 PM2/27/16
to
On 27.02.2016 19:55, Marcel Mueller wrote:
> [snip]
> For type safe operation it would be nice to have a type like
>
>
> template <size_t L, typename P>
> struct opEntry
> { char Name[L]; ///< OP code name
> void (Parser::*Func)(P);///< Parser function to call, receives the
> agrument below.
> P Arg; ///< Arbitrary argument to the parser function.
> };
>
>
> But this does not work since each line of Parser::opcodeMap[] has
> another type P in general and all of these structures must fit into the
> same storage. Of course, there is only a solution when sizeof(P) has an
> upper limit, since this is similar to a union.
>
> Could this be expressed type safe in C++11, without the need to deal
> with pointers, new and delete for each line?

Easy-peasy.

struct Op_entry
{
char const* name;
std::function<void()> parser_func;
};

// ... later
Op_entry e{ "Blah!", [=](){ Parser::assembleADD( Inst::A_ADD ); } };

Hope I got all the braces matched.

Cheers & hth.,

- Alf



Marcel Mueller

unread,
Feb 28, 2016, 4:16:35 AM2/28/16
to
On 27.02.16 21.48, Öö Tiib wrote:
> On Saturday, 27 February 2016 20:55:54 UTC+2, Marcel Mueller wrote:
>> Let's have a dispatch table of an assembler.
>>
>>
>> /// Entry of the OP code lookup table, POD, compatible with binary_search().
>> /// @tparam L maximum length of Name.
>> template <size_t L>
>> struct opEntry
>> { char Name[L]; ///< OP code name
>> void (Parser::*Func)(int);///< Parser function to call, receives the
>> agrument below.
>> int Arg; ///< Arbitrary argument to the parser function.
>> };
>
> I see you use string literals to initialize 'Name' so I don't
> understand why there is that mutable array and that L, do you want
> to dynamically modify it?

No, but the same structure is used for other lookup tables too.
The size differs from table to table and I did not want to define a new
type for each purpose.


>> template <size_t L, typename P>
>> struct opEntry
>> { char Name[L]; ///< OP code name
>> void (Parser::*Func)(P);///< Parser function to call, receives the
>> agrument below.
>> P Arg; ///< Arbitrary argument to the parser function.
>> };
>
> That would not be issue if P was a variant not template argument.
> 'boost::variant<paramtype1,paramtype2,paramtype3>'. You must know
> anyway all the different types of parameters when you initialize
> that 'opcodeMap' of yours.

Not that pretty since there are about a dozen types, but true. The do
not change often. I could use a typedef.

> Btw words like "Map" and "List" may be
> misleading in name of variable since readers sometimes assume
> underlying container.

Well, basically it is a map, implemented as sorted array.
The compiler will tell, if someone tries to invoke .begin() ;-)
But since the member is private this is unlikely.


> No ... union is not type-safe, it is type-punning.

With an appropriate C++ wrapper it is safe.

>> Could this be expressed type safe in C++11, without the need to deal
>> with pointers, new and delete for each line?
>
> Unfortunately the 'variant' is not in C++11.

Hmm, the additional dependency to the boost library could cause some
harm with respect to portability. Especially because I require constexpr
support.


Marcel

Marcel Mueller

unread,
Feb 28, 2016, 4:31:39 AM2/28/16
to
On 27.02.16 21.58, Alf P. Steinbach wrote:
> Easy-peasy.
>
> struct Op_entry
> {
> char const* name;
> std::function<void()> parser_func;
> };
>
> // ... later
> Op_entry e{ "Blah!", [=](){ Parser::assembleADD( Inst::A_ADD ); } };

Indeed, I didn't think of lambdas.

But is this still constexpr? I.e. can this still be a table of compile
time constants in the rodata segment?
If the objects have to be created at application start, it would be
quite costly, there are thousands (not only for op-codes). Target is a
Pi 1, where performance is always an issue.


Marcel

Öö Tiib

unread,
Feb 28, 2016, 6:12:19 AM2/28/16
to
Lambdas are not allowed in constexpr expressions by C++14. There is
proposal N4487. http://open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4487.pdf
I'm not sure what versions of what compilers support it.


Öö Tiib

unread,
Feb 28, 2016, 8:03:55 AM2/28/16
to
On Sunday, 28 February 2016 11:16:35 UTC+2, Marcel Mueller wrote:
> On 27.02.16 21.48, Öö Tiib wrote:
> > On Saturday, 27 February 2016 20:55:54 UTC+2, Marcel Mueller wrote:
> >> Let's have a dispatch table of an assembler.
> >>
> >>
> >> /// Entry of the OP code lookup table, POD, compatible with binary_search().
> >> /// @tparam L maximum length of Name.
> >> template <size_t L>
> >> struct opEntry
> >> { char Name[L]; ///< OP code name
> >> void (Parser::*Func)(int);///< Parser function to call, receives the
> >> agrument below.
> >> int Arg; ///< Arbitrary argument to the parser function.
> >> };
> >
> > I see you use string literals to initialize 'Name' so I don't
> > understand why there is that mutable array and that L, do you want
> > to dynamically modify it?
>
> No, but the same structure is used for other lookup tables too.
> The size differs from table to table and I did not want to define a new
> type for each purpose.

Do you modify that 'Name' dynamically in other lookup tables then?

>
> >> template <size_t L, typename P>
> >> struct opEntry
> >> { char Name[L]; ///< OP code name
> >> void (Parser::*Func)(P);///< Parser function to call, receives the
> >> agrument below.
> >> P Arg; ///< Arbitrary argument to the parser function.
> >> };
> >
> > That would not be issue if P was a variant not template argument.
> > 'boost::variant<paramtype1,paramtype2,paramtype3>'. You must know
> > anyway all the different types of parameters when you initialize
> > that 'opcodeMap' of yours.
>
> Not that pretty since there are about a dozen types, but true. The do
> not change often. I could use a typedef.

Yes, but how else to reach type-safety? All the allowed types
have to be listed somewhere for wrong type to cause code
being ill-formed with diagnostic.

>
> > Btw words like "Map" and "List" may be
> > misleading in name of variable since readers sometimes assume
> > underlying container.
>
> Well, basically it is a map, implemented as sorted array.
> The compiler will tell, if someone tries to invoke .begin() ;-)
> But since the member is private this is unlikely.
>
>
> > No ... union is not type-safe, it is type-punning.
>
> With an appropriate C++ wrapper it is safe.

Writing such wrapper around union is quite interesting task that is why
there are so lot of different variant classes.

>
> >> Could this be expressed type safe in C++11, without the need to deal
> >> with pointers, new and delete for each line?
> >
> > Unfortunately the 'variant' is not in C++11.
>
> Hmm, the additional dependency to the boost library could cause some
> harm with respect to portability. Especially because I require constexpr
> support.

I am not sure how you mean constexpr and portability almost in same
sentence. Constexpr was sort of careful in C++11 and C++14 loosened it
up only slightly. Couple latest versions of top compilers support it
and only Clang is bit ahead of time with it.

My opinion is that if you need best portability and best tested
thing then Boost.Variant, if you need better constexpr support
then Eggs.Variant. Those have quite close interfaces. Both are
licensed under boost's "do whatever you want just don't change the
license".

Eggs.Variant's repo includes interesting Catch unit
test framework if you don't have one. Add unit tests what you
really want and run those on targets that you target.
0 new messages