How to design a C++ class?


Question

I wrote a application in MFC with C++. I need to write a class which can save all the data loaded from the database, These data might contain every kind of data type, such as int, string, byte, boolean, datetime and so on. We might filter, exchange columns, or sort on these data. For example:

int int string bool    double  float .... string
0    1   "a"    false   3.14    3.0        "b"
1    2   "5"    true    3.22    4          "c"

Note: We didn't use SQL to sort or filter, since we have our consideration.

We have wrote the following class, could someone have better suggestion, please write a sample class for use, thanks in advance!

#ifndef __LIST_DATA_MODEL_H__
#define __LIST_DATA_MODEL_H__

#include <vector>
using std::vector;

///implement a pure virtual base class; parameters of function is a void pointer, 

class FieldType
{
public:
 enum {
  TypeChar    = 0,
  TypeString  = 1,

  TypeBool    = 2,

  TypeShort   = 3,
  TypeUShort  = 4,

  TypeInt     = 5,
  TypeUInt    = 6,

  TypeLong    = 7,
  TypeULong   = 8,

  TypeLongLong  = 9,
  TypeULongLong = 10,

  TypeFloat     = 11,
  TypeDouble    = 12
 };
};

template <typename _ValueType, typename _SyncType>
class Column
{
protected:
 CString       m_szFieldName;
 vector<_ValueType> m_vValues;

public:
 Column();
 Column(CString szFieldName);
 Column(const Column& other);
 virtual ~Column();

public:
 virtual BOOL LoadData(...);

public:
 ///This function will call LoadData function to re-load data, 
 ///if subclass this class, please implement your LoadData function 
 ///if you want additional operation when load data.
 CALLBACK BOOL Update();

public:
 const int   ValueCount() const;
 const CString&  FieldName() const;
 const _ValueType&   ValueAt(int iPos) const;

 ///Before you call LoadData function or Update Function, the values will not updated;
 void SetFieldName(const CString& szFieldName);

 void SetValue(const _ValueType& val, int iPos);
};

template<class _Type>
class DataItem
{
protected:
 _Type _value;

public:
 DataItem();
 DataItem(const DataItem& other)
 {
  _value = other._value;
 };
 DataItem(const _Type& val)
 {
  _value = val;
 };
 virtual ~DataItem()
 {
 };

public:
 const _Type& GetValue()
 {
  return _value;
 };
 void SetValue(const _Type& value)
 {
  _value = value;
 };
 void ResetValue()
 {
  _value = _Type();
 };
public:
 bool operator ==(DataItem& right)
 {
  return _value == right._value;
 };
 bool operator <(const DataItem& right)
 {
  return _value < right._value;
 };
 const DataItem& operator =(const DataItem& right)
 {
  if(this == &right)
   return *this;

  _value = right._value;

  return *this;
 };

 virtual DataItem* Clone()
 {
  return new DataItem(*this);
 };
};

typedef DataItem<int>  IntItem;
typedef DataItem<float>  FloatItem;
typedef DataItem<double> DoubleItem;
typedef DataItem<CString> StringItem;
typedef DataItem<bool>      BoolItem;
typedef DataItem<TCHAR>     CharItem;
typedef DataItem<char>      ByteItem;
typedef DataItem<CString>   CStringItem;

#endif
1
0
8/31/2009 8:34:50 AM

Accepted Answer

I am wondering if you are emulating the DB behavior, then does it make sense to store the data in containers of 'type'? Since the data will be accessed via column-names, you need to have containers that store data-values for each column and have column-name to column-type mapping. Anyway if you want to store data along with its type then consider the following approach using 'stringization of enums':-

  1. Create your own enumeration constants for types. Like enum MYTYPE { MYINT, MYFLOAT, ...}
  2. Write-out the DB information after stringizing the data under each stringized-enum.
  3. Read the stringized-enum along with its data into a stringized container like std::vector<string>.
  4. Extract the actual enumeration-type from the stringized-enum and then using a simple switch case statement, covert the stringized-data to actual data.

On how to create stringized enums and use them follow link here and here. The Macro's approach. Or you can use a templatized approcah which requires use of boost something like below (only a hint :-) ).

template<typename ENUM>
class Stringifier<ENUM, typename boost::enable_if<boost::is_enum<ENUM> >::type> {
  static const char * values[]; // array with the enum strings.
  static std::size_t size;      // Number of elements of the ENUM string arrays.
public:
  /// Global static instance of the Stringifier.
  static Stringifier & getInstance()
  {
    static Stringifier globalInstance;
    return globalInstance;
  }
  // Returns the string representation of the ENUM value \a e as a C string.
  // If string is not available an exception is thrown.
  virtual void str(ENUM const & e, std::string & s) const
  {
    if(e >= 0 && e < int(size))
      s = values[e];
    else // throw exception
     ;
  }

  // Returns the ENUM value of the string representation of an ENUM value if possible,
  // ENUM(0) otherwise or ENUM(size) if you like.
  virtual bool value(std::string const & str, ENUM & v) const
  {
    std::size_t i = 0;
    for(; i < size; ++i)
      if(values[i] == str) break;
    bool ok = (i != size);
    v = ok ? ENUM(i) : ENUM(0);
    return ok;
  }
};

Use your enumeration as 'ENUM' in the above class.

NOTE: Stringization kills performance. So this approach is slower.

1
5/23/2017 10:09:41 AM

My first approach would be to use Boost variant or Boost any instead of creating my own.


Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon