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

Template to add members to a class

65 views
Skip to first unread message

Giuliano Bertoletti

unread,
Nov 10, 2013, 3:34:10 AM11/10/13
to

I've some classes with the following layout:

// CCrumb is a string derived type
typedef std::vector<CCrumb *> CRUMBSVECTOR;

class CMatchEntry {
public:
// similar classes may have a different number of CCrumbs
// with different names
CCrumb id;
CCrumb name;
CCrumb logistic;
CCrumb result;
CCrumb details;
CCrumb status;

public:
// there's only this member which
// fills a vector of pointers
void GetCrumbs(CRUMBSVECTOR &cv)
{
cv.clear();

cv.push_back(&id);
cv.push_back(&name);
cv.push_back(&logistic);
cv.push_back(&result);
cv.push_back(&details);
cv.push_back(&status);
}
};

as you see, this class has some members all of the same type which have
to be conveniently returned into a vecotr of pointers for output in an
HTML table.

I need to define other classes with the same layout but with generally a
different number of items.

I do not want to define a vector of crumbs inside a generic class,
beacuse I would then need to use indexes to set members which is prone
to error.

Is there a way to cleverly use template and define classes by specifying
only the names and number of crumbs? I.e.

typedef myTemplate<id,
name,
logistic,
result,
details,
status> CMatchEntry;

typedef myTemplate<id,
tag,
name> CTournamentEntry;


---
Giulio.












Alf P. Steinbach

unread,
Nov 10, 2013, 6:02:20 AM11/10/13
to
On 10.11.2013 09:34, Giuliano Bertoletti wrote:
>
> I've some classes with the following layout:
>
> // CCrumb is a string derived type
> typedef std::vector<CCrumb *> CRUMBSVECTOR;

By reserving ALL UPPERCASE for macros you lower the potential for
getting undesired text substitutions.

It also less of an affront to the eyes.



> class CMatchEntry {

The prefix "C" for a class is a Microsoft abomination.

It has no useful purpose, but it does preclude using that prefix to
indicate something useful such as "const".

I.e. it is of negative practical utility, so you can get a payoff here
simply by /doing less/, ditching those silly Microsoft prefixes.


> public:
> // similar classes may have a different number of CCrumbs
> // with different names
> CCrumb id;
> CCrumb name;
> CCrumb logistic;
> CCrumb result;
> CCrumb details;
> CCrumb status;
>
> public:
> // there's only this member which
> // fills a vector of pointers
> void GetCrumbs(CRUMBSVECTOR &cv)
> {
> cv.clear();
>
> cv.push_back(&id);
> cv.push_back(&name);
> cv.push_back(&logistic);
> cv.push_back(&result);
> cv.push_back(&details);
> cv.push_back(&status);
> }
> };

Uhm, that's not an exception safe way to fill in a vector passed by
reference.

If you absolutely want to do it with an out-argument, do like this:

void get_crumbs( Crumbs_vector& result )
{
Crumbs_vector cv;

cv.push_back( & id );
// Etc.

// Any exception above won't affect caller's data.
// Now we swap, which is a non-throwing operation.
cv.swap( result );

// And here the destructor takes care of the original.
}

But much better, unless timings show that this is unacceptably slow
(which would probably mean you're using an outdated compiler), do ...

auto crumbs()
-> Crumbs_vector
{
Crumbs_vector cv;

cv.push_back( & id );
// Etc.

return cv;
}


> as you see, this class has some members all of the same type which have
> to be conveniently returned into a vecotr of pointers for output in an
> HTML table.
>
> I need to define other classes with the same layout but with generally a
> different number of items.
>
> I do not want to define a vector of crumbs inside a generic class,
> beacuse I would then need to use indexes to set members which is prone
> to error.
>
> Is there a way to cleverly use template and define classes by specifying
> only the names and number of crumbs? I.e.
>
> typedef myTemplate<id,
> name,
> logistic,
> result,
> details,
> status> CMatchEntry;
>
> typedef myTemplate<id,
> tag,
> name> CTournamentEntry;

No, templates cannot handle names.

However, macros can handle names, and as of C++11 there is direct
language support for handling a variable number of arguments.


[code]
#include <rfc/macro_magic/apply.h> // MM_APPLY

#define GEN_CRUMBS_MEMBER( name ) \
Crumb name;

#define GEN_CRUMBS_PUSHBACK( name ) \
result.push_back( &name );

#define GEN_CRUMBS_CLASS( classname, ... ) \
struct classname \
{ \
MM_APPLY( GEN_CRUMBS_MEMBER, __VA_ARGS__ ) \
\
Crumbs_vector crumbs() \
{ \
Crumbs_vector result; \
MM_APPLY( GEN_CRUMBS_PUSHBACK, __VA_ARGS__ ) \
return result; \
} \
};

GEN_CRUMBS_CLASS( MyClass, a, b, c )
[code]

[example]
[D:\dev\test]
> g++ -E foo.cpp | find "struct"
struct MyClass { Crumb a; Crumb b; Crumb c; Crumbs_vector crumbs() {
Crumbs_vector result; result.push_back( &a ); result.push_back( &b );
result.push_back( &c ); return result; } };

[D:\dev\test]
> _
[/example]


So, all you have to do is to define the macro MM_APPLY. :-)

That's not hard for a standard-conforming compiler, but both the Visual
C++ and the g++ preprocessor have some annoying bugs that gets in the way.

The first of these bugs (I'm not sure anymore which compiler) is the
cause of the otherwise perplexing use of both MM_INVOKE and MM_INVOKE_B
in the code below -- and for that matter, the otherwise perplexing use
of MM_INVOKE in the first place:


[code file="rfc/macro_magic/apply.h"]
#pragma once
// Copyright (c) 2013 Alf P. Steinbach.

#include <rfc/macro_magic/nargs.h> // MM_INVOKE, MM_INVOKE_B,
MM_CONCAT, MM_NARGS

#define MM_APPLY_1( macroname, a1 ) \
MM_INVOKE_B( macroname, (a1) )

#define MM_APPLY_2( macroname, a1, a2 ) \
MM_INVOKE_B( macroname, (a1) ) \
MM_APPLY_1( macroname, a2 )

#define MM_APPLY_3( macroname, a1, a2, a3 ) \
MM_INVOKE_B( macroname, (a1) ) \
MM_APPLY_2( macroname, a2, a3 )

#define MM_APPLY_4( macroname, a1, a2, a3, a4 ) \
MM_INVOKE_B( macroname, (a1) ) \
MM_APPLY_3( macroname, a2, a3, a4 )

#define MM_APPLY_5( macroname, a1, a2, a3, a4, a5 ) \
MM_INVOKE_B( macroname, (a1) ) \
MM_APPLY_4( macroname, a2, a3, a4, a5 )

#define MM_APPLY_6( macroname, a1, a2, a3, a4, a5, a6 ) \
MM_INVOKE_B( macroname, (a1) ) \
MM_APPLY_5( macroname, a2, a3, a4, a5, a6 )

#define MM_APPLY_7( macroname, a1, a2, a3, a4, a5, a6, a7 ) \
MM_INVOKE_B( macroname, (a1) ) \
MM_APPLY_6( macroname, a2, a3, a4, a5, a6, a7 )

#define MM_APPLY_8( macroname, a1, a2, a3, a4, a5, a6, a7, a8 ) \
MM_INVOKE_B( macroname, (a1) ) \
MM_APPLY_7( macroname, a2, a3, a4, a5, a6, a7, a8 )

#define MM_APPLY_9( macroname, a1, a2, a3, a4, a5, a6, a7, a8, a9 ) \
MM_INVOKE_B( macroname, (a1) ) \
MM_APPLY_8( macroname, a2, a3, a4, a5, a6, a7, a8, a9 )

#define MM_APPLY_10( macroname, \
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 \
) \
MM_INVOKE_B( macroname, (a1) ) \
MM_APPLY_9( macroname, a2, a3, a4, a5, a6, a7, a8, a9, a10 )

#define MM_APPLY_11( macroname, \
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, \
a11 \
) \
MM_INVOKE_B( macroname, (a1) ) \
MM_APPLY_10( macroname, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11 )

#define MM_APPLY_12( macroname, \
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, \
a11, a12 \
) \
MM_INVOKE_B( macroname, (a1) ) \
MM_APPLY_11( macroname, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 )

#define MM_APPLY_13( macroname, \
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, \
a11, a12, a13 \
) \
MM_INVOKE_B( macroname, (a1) ) \
MM_APPLY_12( macroname, \
a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, \
a12, a13 \
)

#define MM_APPLY_14( macroname, \
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, \
a11, a12, a13, a14 \
) \
MM_INVOKE_B( macroname, (a1) ) \
MM_APPLY_13( macroname, \
a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, \
a12, a13, a14 \
)

#define MM_APPLY_15( macroname, \
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, \
a11, a12, a13, a14, a15 \
) \
MM_INVOKE_B( macroname, (a1) ) \
MM_APPLY_14( macroname, \
a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, \
a12, a13, a14, a15 \
)

#define MM_APPLY_16( macroname, \
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, \
a11, a12, a13, a14, a15, a16 \
) \
MM_INVOKE_B( macroname, (a1) ) \
MM_APPLY_15( macroname, \
a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, \
a12, a13, a14, a15, a16 \
)

#define MM_APPLY_17( macroname, \
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, \
a11, a12, a13, a14, a15, a16, a17 \
) \
MM_INVOKE_B( macroname, (a1) ) \
MM_APPLY_16( macroname, \
a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, \
a12, a13, a14, a15, a16, a17 \
)

#define MM_APPLY_18( macroname, \
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, \
a11, a12, a13, a14, a15, a16, a17, a18 \
) \
MM_INVOKE_B( macroname, (a1) ) \
MM_APPLY_17( macroname, \
a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, \
a12, a13, a14, a15, a16, a17, a18 \
)

#define MM_APPLY_19( macroname, \
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, \
a11, a12, a13, a14, a15, a16, a17, a18, a19 \
) \
MM_INVOKE_B( macroname, (a1) ) \
MM_APPLY_18( macroname, \
a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, \
a12, a13, a14, a15, a16, a17, a18, a19 \
)

#define MM_APPLY_20( macroname, \
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, \
a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 \
) \
MM_INVOKE_B( macroname, (a1) ) \
MM_APPLY_19( macroname, \
a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, \
a12, a13, a14, a15, a16, a17, a18, a19, a20 \
)

#define MM_APPLY_21( macroname, \
a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, \
a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, \
a21 \
) \
MM_INVOKE_B( macroname, (a1) ) \
MM_APPLY_20( macroname, \
a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, \
a12, a13, a14, a15, a16, a17, a18, a19, a20, a21 \
)

#define MM_APPLY( macroname, ... ) \
MM_INVOKE( \
MM_CONCAT( MM_APPLY_, MM_NARGS( __VA_ARGS__ ) ), \
( macroname, __VA_ARGS__ ) \
)
[/code]


The "MM_NARGS" macro simply counts the number of arguments to a macro:


[code file="rfc/macro_magic/nargs.h"]
#pragma once
// Copyright (c) 2013 Alf P. Steinbach.
//
// The MM_NARGS macro evaluates to the number of arguments that have been
// passed to it.
//
// Based on original code by Laurent Deniau,
// "__VA_NARG__," 17 January 2006, <comp.std.c> (29 November 2007).
//
https://groups.google.com/forum/?fromgroups=#!topic/comp.std.c/d-6Mj5Lko_s

#include <rfc/macro_magic/invoke.h> // MM_INVOKE
#include <rfc/macro_magic/concat.h> // MM_CONCAT

#define MM_ARG_N( \
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
_61,_62,_63,N,...) N

#define MM_RSEQ_N() \
63,62,61,60, \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9,8,7,6,5,4,3,2,1,0

#ifdef MM_USE_ORIGINAL_DEFINITION
#define MM_NARGS_(...) MM_ARG_N( __VA_ARGS__ )
#define MM_NARGS( ...) MM_NARGS_( __VA_ARGS__, MM_RSEQ_N() )
#else
#define MM_NARGS_AUX( ... ) MM_INVOKE( MM_ARG_N, ( __VA_ARGS__ ) )
#define MM_NARGS( ... ) MM_NARGS_AUX( __VA_ARGS__, MM_RSEQ_N() )
#endif
[/code]


Then, for completeness, the MM_INVOKE and MM_CONCAT macros:


[code file="rfc/macro_magic/invoke.h"]
#pragma once
// Copyright (c) 2013 Alf P. Steinbach.

#define MM_INVOKE( macro, args ) macro args
#define MM_INVOKE_B( macro, args ) macro args // For nested
invocation with g++.
[/code]

[code file="rfc/macro_magic/concat.h"]
#pragma once
// Copyright (c) 2013 Alf P. Steinbach.

#define MM_CONCAT__( a, b ) a ## b
#define MM_CONCAT_( a, b ) MM_CONCAT__( a, b )
#define MM_CONCAT( a, b ) MM_CONCAT_( a, b )
[/code]


And that's it.

Wasn't that simple, yes?


Cheers & hth.,

- Alf

David Harmon

unread,
Nov 11, 2013, 2:30:24 PM11/11/13
to
On Sun, 10 Nov 2013 12:02:20 +0100 in comp.lang.c++, "Alf P.
Steinbach" <alf.p.stein...@gmail.com> wrote,
>The prefix "C" for a class is a Microsoft abomination.
>It has no useful purpose, but it does preclude using that prefix to
>indicate something useful such as "const".

It serves the very useful purpose of enabling someone reading your
code to distinguish Microsoft classes from your own classes without
remembering them all. Never using class names beginning with "C"
for your own classes is a small price to pay for that. Of course
namespaces are a better solution, but many Microsoft APIs were
defined before c++ namespaces existed.

Juha Nieminen

unread,
Nov 12, 2013, 3:01:20 AM11/12/13
to
Giuliano Bertoletti <gbe3...@libero.it> wrote:
> I need to define other classes with the same layout but with generally a
> different number of items.

It sounds like you essentially want std::tuple.

--- news://freenews.netfront.net/ - complaints: ne...@netfront.net ---

James Kanze

unread,
Nov 12, 2013, 5:23:19 AM11/12/13
to
It's fairly short for a prefix. Still, it doesn't exclude using
your own classes beginning with a "C", as long as the "C" isn't
a prefix in your classes. In the Microsoft classes, it will be
a "C", followed by a word which also begins with an upper case;
it's immediately visible that the "C" is a prefix. In your own
classes, if the "C" isn't a prefix, it will normally be followed
by a lower case, and will effectively appear as part of the
name, and not an unrelated prefix.

--
James

Öö Tiib

unread,
Nov 12, 2013, 6:44:44 AM11/12/13
to
When I generate classes then I usually do it with some (often self-made) external
code generator tool. Preprocessor metaprogramming and/or template
metaprogramming feel hellishly cryptic if to compare with little Python script that
any kid can read.

> I do not want to define a vector of crumbs inside a generic class,
> beacuse I would then need to use indexes to set members which is prone
> to error.
>
> Is there a way to cleverly use template and define classes by specifying
> only the names and number of crumbs? I.e.

Ok ... so hard way then? Smart choice. Boost.Serialization can serialise any
object to XML and back from XML to object with not too much work for user.
Writing similar thing is another story of course. ;-) Download it and study.
Every preprocessor, template and pointer-to-member trick you need (and
way more than you need) is used there. You are lot more clever after
studying it than you were before. ;-)
0 new messages