HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Should I use an object or type as trait?

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
objecttypetraitshoulduse

Problem

Edit: Disclaimer: I am new to templates and traits.
I apologize in advance if this is a bad question.

I am refactoring two similar classes with small differences strewn all over them into one template class (or I am trying to anyway).
This is something I just wrote. (I hope I didn't make any mistakes renaming everything.)

// declaration - empty
template 
struct SettingsTraits;

// specialization for type CFoo
template <>
struct SettingsTraits
{
  typedef CFooRelated RelatedType;
  typedef CAttribute & (CDataSource::*AttrGetter)();
  static CAttribute & (CDataSource::*GetAttr)();
};

SettingsTraits::AttrGetter SettingsTraits::GetAttr = &CDataSource::GetFooAttr;

// specialization for type CBar    
template <>
struct SettingsTraits
{
  typedef CBarRelated RelatedType;
  typedef CAttribute & (CDataSource::*AttrGetter)();
  static CAttribute & (CDataSource::*GetAttr)();
};

SettingsTraits::AttrGetter SettingsTraits::GetAttr = &CDataSource::GetBarAttr;


So that I can write something like this

CAttribute::RelatedType> attr = (datasource.*SettingsTraits::GetAttr)();


But now I am wondering if I shouldn't just have created two different structs and passed those as template parameters instead.

I imagine the calling code would end up looking like this:

CAttribute attr = FooTraits::GetAttr(datasource);


So probably somewhat cleaner but I haven't tried it so I don't know if it will actually work.
(I should probably replace the function pointer with a simple one line function that calls the appropriate function for each type in any case.)

Which alternative should I chose? What are the benefits/drawbacks of each?

Does it even make sense to have functions in traits? Should I maybe just use free template functions instead?

Solution

Yes, the std way of doing things in C++ would be to provide static member functions of your traits type. Consider:

// declaration - empty
template 
struct SettingsTraits;

// specialization for type CFoo
template <>
struct SettingsTraits
{
  typedef CFooRelated RelatedType;
  static CAttribute& GetAttrFrom(CDataSource& ds) { return ds.GetFooAttr(); }
};

// specialization for type CBar    
template <>
struct SettingsTraits
{
  typedef CBarRelated RelatedType;
  static CAttribute& GetAttrFrom(CDataSource& ds) { return ds.GetBarAttr(); }
};

typedef SettingsTraits TRAITS;
CAttribute attr = TRAITS::GetAttrFrom(datasource);


Notice that this way of doing things doesn't involve global objects or values at all; it's all done entirely within the type system. The compiler can look at the line TRAITS::GetAttrFrom(datasource) and statically know that it's equivalent to datasource.GetFooAttr(), which means the whole thing compiles down to a single call instruction. Your old code would have required a trip to memory in order to load the value of the pointer SettingsTraits::GetAttr.

Rule of thumb: you should never need to construct a type-traits object at all. If you do, you're probably doing it wrong.

Code Snippets

// declaration - empty
template <typename SETTINGS>
struct SettingsTraits;

// specialization for type CFoo
template <>
struct SettingsTraits<CFoo>
{
  typedef CFooRelated RelatedType;
  static CAttribute<RelatedType>& GetAttrFrom(CDataSource& ds) { return ds.GetFooAttr(); }
};

// specialization for type CBar    
template <>
struct SettingsTraits<CBar>
{
  typedef CBarRelated RelatedType;
  static CAttribute<RelatedType>& GetAttrFrom(CDataSource& ds) { return ds.GetBarAttr(); }
};

typedef SettingsTraits<SETTINGS> TRAITS;
CAttribute<TRAITS::RelatedType> attr = TRAITS::GetAttrFrom(datasource);

Context

StackExchange Code Review Q#27361, answer score: 2

Revisions (0)

No revisions yet.