[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c++

User defined class meta data (type traits?

greek_bill

10/29/2008 8:53:00 PM

Hi,

I'm trying to develop a system where I can register some data/
information about a class. For example

// ClassInfo.h
template <class T>
struct ClassInfo
{
static const std::string tagName;
static const int version;
static const bool isConfigurable;
};

// FooBar.h
class FooBar {...};

// FooBar.cpp
template<> const std::string ClassInfo<FooBar>::tagName = "FooBar";
template<> const int ClassInfo<FooBar>::version = 1;
template<> const bool ClassInfo<FooBar>::isConfigurable = false;

// main.cpp
....
#include "FooBar.h"
if (ClassInfo<FooBar>::isConfigurable)
{...}

This does what I originally wanted, which is to allow me to associate
meta-data with some arbitrary class, and have that accessible
throughout the code.

The problem is that since the above relies on static variables being
initialized at runtime, I cannot use any of the data as template
arguments. For example I'd like to be able to do the following :


template<class T, bool isConfigurable>
struct SomeAlgorithmImp
{
static void Func()
{
//...
};
};

template<class T>
struct SomeAlgorithmImp<T, true>
{
static void Func()
{
//...
};
};

template<class T>
struct SomeAlgorithm
{
static void Func()
{
SomeAlgorithmImp<T, ClassInfo<T>::isConfigurable>::Func();
}
};

SomeAlgorithm<FooBar>::Func();


The problem is that ClassInfo<T>::isConfigurable is not a 'compile
time constant expression'. (it actually can be used as a template
argument but only in the same translation unit as the definition of
the static variable - which limits its usefulness).

Any ideas on how I might be able to achieve this?

At some point I naively thought I can put the following in the header
instead of the translation unit :

template<> const bool ClassInfo<FooBar>::isConfigurable = false;

which of course works for using it as a template argument, but also
gives you multiple symbols during linking (yet bizarrely VC8 doesn't
seem to mind, g++ does).

I also tried making the ClassInfo class contain a struct which is
redifined per 'T', e.g.

template<class T>
struct ClassInfo
{
struct IsConfigurable;
};

then :
template<>
struct ClassInfo<Foo>::IsConfigurable
{
enum { Value = 1 };
};

This would have almost worked...had it not been for namespaces.
ClassInfo<T>::IsConfigurable must be defined in the same namespace as
ClassInfo...which in my case is impossible to guarantee.

I could also override the entire contents of ClassInfo. this would let
me to initialize integral types (like the int and bool members above)
within the class body thus avoiding the linker errors. However, this
approach also suffers from the namespace issues (the specialization of
a class must be in the same namespace as the class template itself)
(another thing that VC8 isn't too fussed about either!)

Any other ideas? At the moment I'm leaning towards trying to work
around my design's namespace complications , which would allow me to
use the child struct method above. Either that, or just go with a run
time solution to keep things simple.

Many thanks,

Bill
5 Answers

anon

10/30/2008 8:55:00 AM

0

greek_bill wrote:
> Hi,
>
> I'm trying to develop a system where I can register some data/
> information about a class. For example
>
> // ClassInfo.h
> template <class T>
> struct ClassInfo
> {
> static const std::string tagName;
> static const int version;
> static const bool isConfigurable;
> };
>
> // FooBar.h
> class FooBar {...};
>
> // FooBar.cpp
> template<> const std::string ClassInfo<FooBar>::tagName = "FooBar";
> template<> const int ClassInfo<FooBar>::version = 1;
> template<> const bool ClassInfo<FooBar>::isConfigurable = false;
>
> // main.cpp
> ...
> #include "FooBar.h"
> if (ClassInfo<FooBar>::isConfigurable)
> {...}
>
> This does what I originally wanted, which is to allow me to associate
> meta-data with some arbitrary class, and have that accessible
> throughout the code.
>

[...]

Does the next example demonstrates what you want?

#include <string>
#include <iostream>

struct A1
{
};
struct A2
{
};

template < class T >
struct At
{
};

template<>
struct At< A1 >
{
static std::string Get()
{
return "A1";
}
};
template<>
struct At< A2 >
{
static std::string Get()
{
return "A2";
}
};
int main()
{
std::cout << At< A1 >::Get() << std::endl;
}



greek_bill

10/30/2008 7:57:00 PM

0

>
> Does the next example demonstrates what you want?
>

No, not really. What you're doing is overriding (or rather providing)
Get() in a template specialization and the use that at run time. What
I'd like to do is override/provide something (e.g. an int or a bool)
in a template specialization and use that as a template argument at
compile time.

Hendrik Schober

10/31/2008 4:10:00 PM

0

greek_bill wrote:
> Hi,
>
> I'm trying to develop a system where I can register some data/
> information about a class. For example
>
> [code]
>
> This does what I originally wanted, which is to allow me to associate
> meta-data with some arbitrary class, and have that accessible
> throughout the code.
>
> The problem is that since the above relies on static variables being
> initialized at runtime, I cannot use any of the data as template
> arguments. [...]
>
> Any ideas on how I might be able to achieve this?
>
> At some point I naively thought I can put the following in the header
> instead of the translation unit :
>
> template<> const bool ClassInfo<FooBar>::isConfigurable = false;
>
> which of course works for using it as a template argument, but also
> gives you multiple symbols during linking (yet bizarrely VC8 doesn't
> seem to mind, g++ does).

You could work around this by implementing this in some 'ClassInfoImp'
which has another template parameter that you don't specialize (and
which doesn't have any semantic meaning). This makes 'ClassInfoImp<Foo>'
a partial specialization, which can/needs to have its members defined
in the header.
Then you derive your 'ClassInfo<Foo>' from 'ClassInfoImp<Foo,DummyType>'
where 'DummyType' is the one without semantics (and could be 'void*' or
or 'Foo' 'struct DummyType {}' or whatever you prefer).

> I also tried making the ClassInfo class contain a struct which is
> redifined per 'T', e.g.
>
> template<class T>
> struct ClassInfo
> {
> struct IsConfigurable;
> };
>
> then :
> template<>
> struct ClassInfo<Foo>::IsConfigurable
> {
> enum { Value = 1 };
> };
>
> This would have almost worked...had it not been for namespaces.
> ClassInfo<T>::IsConfigurable must be defined in the same namespace as
> ClassInfo...which in my case is impossible to guarantee.

Why is it impossible?

> I could also override the entire contents of ClassInfo. this would let
> me to initialize integral types (like the int and bool members above)
> within the class body thus avoiding the linker errors. [...]

I don't understand this.

> Any other ideas? At the moment I'm leaning towards trying to work
> around my design's namespace complications , which would allow me to
> use the child struct method above. Either that, or just go with a run
> time solution to keep things simple.

Compile-time traits are a great tool. :)

> Many thanks,
>
> Bill

Schobi

greek_bill

11/2/2008 7:07:00 PM

0

I like the partial base class specialization...good idea! :)

but I think it suffers from the same namespace problems. In my setup I
have something like :

namespace Core
{
// ClassInfo template definition here
}

namespace Extension
{
class Foo;

// Ideally I'd like to have the Foo specialization here
template<> ClassInfo<Foo> { ... };
}

The above fails to compile because the specialization of ClassInfo
appears in a different namespace ('Extension') than its definition. To
get this to work, I'd have to have the following :

namespace Extension
{
class Foo;
}

namespace Core
{
template<> ClassInfo<Extension::Foo> { ... };
}

While this is certainly possible, it forces a code layout restriction
that I'm not comfortable with. Ideally I'd like to keep the
specialization on Foo and Foo's definition as close as possible.

I admit, this isn't a huge problem, but if I could work around it that
I'd rather I did!


btw, I've moved on from this. What I did in the end is to 'discover'
whether a class is 'configurable' by checking for the presence of a
certain function. I find that discovering existing features (traits?)
of a class is generally easier than associating new ones (like I've
been describing above). Most of the relevant material I've read on
this topic (Modern C++, Boost::type_traits, etc) focus on discovering
features about a class.

Also, as it turns out, in my case it's better to decide whether a
class is configurable based on the presence of a Configure() function.
While I was trying to get the bool 'IsConfigurable' to work, I found
myself adding a static check (compile time assert) to ensure that if a
class is declared as configurable then it also provides a configure
function.

Anyway, thanks for the good idea though! :)

> Compile-time traits are a great tool. :)

Yes, they are!

Bill

Hendrik Schober

11/3/2008 5:13:00 PM

0

greek_bill wrote:
> I like the partial base class specialization...good idea! :)
>
> but I think it suffers from the same namespace problems. [...]

Yes, it does. Which is the reason I asked why this such
a problem in your case.

> [...] To
> get this to work, I'd have to have the following :
>
> namespace Extension
> {
> class Foo;
> }
>
> namespace Core
> {
> template<> ClassInfo<Extension::Foo> { ... };
> }
>
> While this is certainly possible, it forces a code layout restriction
> that I'm not comfortable with. Ideally I'd like to keep the
> specialization on Foo and Foo's definition as close as possible.
>
> I admit, this isn't a huge problem, but if I could work around it that
> I'd rather I did!

If you don't need a fully defined class at the place you
define the traits for that class, you could use a macro that
declares the class and then defines its traits. If the macro
is well documented to be used only in global namespace (and
especially if any other use leads to compile-time errors) it
shouldn't be a problem.
OTOH, when you want to have each class close to its traits,
why don't you define those traits within the class (like STL
iterators and 'std::iterator_traits' work together)?

> btw, I've moved on from this. What I did in the end is to 'discover'
> whether a class is 'configurable' by checking for the presence of a
> certain function. I find that discovering existing features (traits?)
> of a class is generally easier than associating new ones (like I've
> been describing above). Most of the relevant material I've read on
> this topic (Modern C++, Boost::type_traits, etc) focus on discovering
> features about a class.

Yes, automatically finding out about features, if possible,
is always easier. Especially when later you have to add a
class and mustn't forget about all the traits that come
with it.

> [...]
> Bill

Schobi