I'm looking for a way to write certain kinds of classes without duplication or unnecessary verbosity. Because there will be many of them in a single file and I want them to be readable. I'm trying to decide if I should use the preprocessor or if I should write a script to do the translation before compilation. I would like something like this (how I think it might look): CLASS(Foo, FROM(Bar)) CONSTRUCT() CONSTRUCT(PAR(int, a, A), PAR(bool, b, B)) int A; bool B; END_CLASS to automatically become something like this: class Foo : public Bar, public shared_from_this<Foo> { private: Foo() {} public: static shared_ptr<Foo> construct() { return shared_ptr<Foo>(new Foo()); } private: Foo(int a, bool b) : A(a), B(b) {} public: static shared_ptr<Foo> construct(int a, bool b) { return shared_ptr<Foo>(new Foo(a, b)); } int A; bool B; }; Is such a thing possible? You see, there are a few things happening there: * Variable length list, reproduced more than once in different ways. * Foo is remembered to use in the constructor name. What would the original code look like if we could make this work? And is there something in the boost preprocessor library that can help me? Thanks in advance! -- Michiel Helvensteijn
On Sun, 2009-01-04 at 22:53 +0100, Michiel Helvensteijn wrote:
I would like something like this (how I think it might look):
CLASS(Foo, FROM(Bar)) CONSTRUCT() CONSTRUCT(PAR(int, a, A), PAR(bool, b, B)) int A; bool B; END_CLASS
to automatically become something like this:
class Foo : public Bar, public shared_from_this<Foo> { private: Foo() {} public: static shared_ptr<Foo> construct() { return shared_ptr<Foo>(new Foo()); } private: Foo(int a, bool b) : A(a), B(b) {} public: static shared_ptr<Foo> construct(int a, bool b) { return shared_ptr<Foo>(new Foo(a, b)); } int A; bool B; };
Have you considered how boost::tuple might help ?
A boost::tuple
Tim Day wrote:
Have you considered how boost::tuple might help ? A boost::tuple
would seem to provide most of the above. A simple template could provide a tuple-wrapping class restricting allocation to the shared_ptr--returning construct method. OK so you wouldn't get to name the members, and the construct method itself would presumably need to take a tuple (unless you provide sufficient templated member functions to handle as many arguments as necessary). I don't know how important those aspects are to you though.
Quite important, actually. I suspect using boost tuples in a wrapper would result in the same amount of code as unboosted code, if not more.
In my experience, trying to be too clever with the preprocessor results in a nastier mess than the problem you were originally trying to solve.
Hm. Not really. I've already done some things with the preprocessor that significantly shortened and clarified my code. The only downside for me seems to be that syntax highlighting will not work. -- Michiel Helvensteijn
AMDG Michiel Helvensteijn wrote:
I'm looking for a way to write certain kinds of classes without duplication or unnecessary verbosity. Because there will be many of them in a single file and I want them to be readable.
I'm trying to decide if I should use the preprocessor or if I should write a script to do the translation before compilation. I would like something like this (how I think it might look):
CLASS(Foo, FROM(Bar)) CONSTRUCT() CONSTRUCT(PAR(int, a, A), PAR(bool, b, B)) int A; bool B; END_CLASS
to automatically become something like this:
class Foo : public Bar, public shared_from_this<Foo> { private: Foo() {} public: static shared_ptr<Foo> construct() { return shared_ptr<Foo>(new Foo()); } private: Foo(int a, bool b) : A(a), B(b) {} public: static shared_ptr<Foo> construct(int a, bool b) { return shared_ptr<Foo>(new Foo(a, b)); } int A; bool B; };
Is such a thing possible? You see, there are a few things happening there:
* Variable length list, reproduced more than once in different ways.
Preprocessor sequences would work.
* Foo is remembered to use in the constructor name.
Not possible.
What would the original code look like if we could make this work?
CLASS(Foo, FROM(Bar)) CONSTRUCT(Foo, BOOST_PP_EMPTY_SEQ) CONSTRUCT(Foo, (int, a, A)(bool, b, B)) int A; bool B; END_CLASS is possible.
And is there something in the boost preprocessor library that can help me?
The following at least might be useful http://www.boost.org/libs/preprocessor/doc/ref/seq_enum.html http://www.boost.org/libs/preprocessor/doc/ref/tuple_elem.html In Christ, Steven Watanabe
Steven Watanabe wrote:
* Foo is remembered to use in the constructor name.
Not possible.
What would the original code look like if we could make this work?
CLASS(Foo, FROM(Bar)) CONSTRUCT(Foo, BOOST_PP_EMPTY_SEQ) CONSTRUCT(Foo, (int, a, A)(bool, b, B)) int A; bool B; END_CLASS
is possible.
What about: CLASS(Foo, FROM(Bar), CONSTRUCT(BOOST_PP_EMPTY_SEQ) CONSTRUCT((int, a, A)(bool, b, B)) int A; bool B; ) Might that not be a way to avoid the Foo repetition?
And is there something in the boost preprocessor library that can help me?
The following at least might be useful http://www.boost.org/libs/preprocessor/doc/ref/seq_enum.html http://www.boost.org/libs/preprocessor/doc/ref/tuple_elem.html
Thanks! I'll have a look there. -- Michiel Helvensteijn
What about:
CLASS(Foo, FROM(Bar), CONSTRUCT(BOOST_PP_EMPTY_SEQ) CONSTRUCT((int, a, A)(bool, b, B)) int A; bool B; )
Might that not be a way to avoid the Foo repetition?
It would have to look like CLASS( Foo, FROM(Bar) , CONSTRUCT( BOOST_PP_EMPTY_SEQ ) CONSTRUCT( (int, a, A)(bool, b, B) ) , int A ; bool B ; ) where you have #define CONSTRUCT(sequence) sequence and the CLASS macro does all the work. The macro implementation would be slightly more complicated because of the nested FOR_EACHs (degrading maintainability) and debuggability will be impaired, but perhaps still better than introducing an external code generator to your build setup. Nb it seems like the 'a' and 'b' are superfluous - the constructor parameters could be written something like BOOST_PP_CAT(membervarname , _ ).
Pete Bartlett wrote:
What about:
CLASS(Foo, FROM(Bar), CONSTRUCT(BOOST_PP_EMPTY_SEQ) CONSTRUCT((int, a, A)(bool, b, B)) int A; bool B; )
Might that not be a way to avoid the Foo repetition?
It would have to look like
CLASS( Foo, FROM(Bar) , CONSTRUCT( BOOST_PP_EMPTY_SEQ ) CONSTRUCT( (int, a, A)(bool, b, B) ) , int A ; bool B ; )
where you have
#define CONSTRUCT(sequence) sequence
Why wouldn't my example work?
and the CLASS macro does all the work. The macro implementation would be slightly more complicated because of the nested FOR_EACHs (degrading maintainability) and debuggability will be impaired, but perhaps still better than introducing an external code generator to your build setup.
You know, I just realized it's not possible after all, since I will also add enums and structs as sub-types sometimes. They contain comma's, which would break the macro-call. I'll use the END_CLASS notation and accept the Foo repetition. :-)
Nb it seems like the 'a' and 'b' are superfluous - the constructor parameters could be written something like BOOST_PP_CAT(membervarname , _ ).
Good point! How could I have missed that one? Thanks! -- Michiel Helvensteijn
Quoting Michiel Helvensteijn
Pete Bartlett wrote:
What about:
CLASS(Foo, FROM(Bar), CONSTRUCT(BOOST_PP_EMPTY_SEQ) CONSTRUCT((int, a, A)(bool, b, B)) int A; bool B; )
Might that not be a way to avoid the Foo repetition?
It would have to look like
CLASS( Foo, FROM(Bar) , CONSTRUCT( BOOST_PP_EMPTY_SEQ ) CONSTRUCT( (int, a, A)(bool, b, B) ) , int A ; bool B ; )
where you have
#define CONSTRUCT(sequence) sequence
Sorry this should have been #define CONSTRUCT(sequence) (sequence)
Why wouldn't my example work?
For each occurence of CONSTRUCT you need to print Foo. The only thing that /can/ print Foo is the CLASS macro so you end up with the constraint that one of the arguments to the CLASS macro is a sequence of CONSTRUCTs so that CLASS can be partially implemented in terms of a BOOST_PP_FOR_EACH over that sequence. Your third argument is not a sequence, but with the correction mine is.
You know, I just realized it's not possible after all, since I will also add enums and structs as sub-types sometimes. They contain comma's, which would break the macro-call. I'll use the END_CLASS notation and accept the Foo repetition. :-)
Well, don't underestimate the power of the preprocessor and in particular the Boost.Preprocessor library. Appropriate use of parentheses can sort out those commas in many cases. And in the harder cases, there is also BOOST_PP_COMMA. Having said that, when the macro design gets this complicated there is, in my experience, usually an under-use of templates in the underlying code.
participants (5)
-
Michiel Helvensteijn
-
Pete Bartlett
-
Peter Bartlett
-
Steven Watanabe
-
Tim Day