Java 1.5提供了一个新的类型 - Enum,区别于传统C/C++的整型语义的枚举,Java的Enum为使用多态分支来取代 if/else 和 switch 这样的类型码相依逻辑的选择代码块提供了一个轻量级的实现,并同时具备传统的整型枚举语义与额外的便利的字符串语义 (可以获得枚举量对应的整型序号值与字符串值 oridinal toString,或通过字符串值获得对应的枚举量 valueOf)。
我写了一个类似的C++版本,用在自己的项目中,下面是它的实现,因为使用了QT库,所以string,vector,map都是使用QT的实现,不过也很容易替换为标准库的实现,于Java的实现稍有不同的是使用name而不是toString来获得枚举量字符串值,toString提供了更丰富的表示用于内部状态打印。
Enum.h
Enum.cpp
头文件里面定义了一个 DECLARE_ENUM宏,用来为从Enum类派生的枚举类型生成一个构造函数和values 与valueOf 函数。
因为不能像Java一样由编译器自动生成辅助代码,所以使用上(枚举类型和枚举量的定义)会比Java要复杂一些,不过相对而言,也比较灵活一些。
下面是定义一个枚举类型的示例:
接下来我们在实际使用中就可以为这个枚举类型派生出多个子类型,并为每个子类型生成一个单件常量(枚举量),类似这样:
ISO13660Analyser.h
如果需要访问某个特定的枚举量,子类型的单件声明也可以放在父类型里,像Java的实现一样,不过并没有Java那样必须如此的限制。
以上的实现在UI界面的呈现上会有巨大的便利,我们可以通过枚举类型的values调用获得整个枚举量的集合,把它们的名字放在列表框或者下拉选框中让用户选择。然后把用户的选择使用多态自动派遣到具体的实现逻辑中去。
我写了一个类似的C++版本,用在自己的项目中,下面是它的实现,因为使用了QT库,所以string,vector,map都是使用QT的实现,不过也很容易替换为标准库的实现,于Java的实现稍有不同的是使用name而不是toString来获得枚举量字符串值,toString提供了更丰富的表示用于内部状态打印。
Enum.h
#pragma
once
#include < QString >
#include < QMap >
#include < QVector >
#include < iosfwd >
namespace milk
{
class Enum
{
public:
/** Dump all Enum information out, only for debug. */
static void dump();
static void dump(std::ostream&);
protected:
/** A safe storage for store all enumerate items. */
static QMap<QString, QVector<const Enum*>>& EMAP();
/** Get all Enum items of the given Enum type. */
static QVector<const Enum*> values(const QString&);
/** Get the Enum items by it's type and name. */
static const Enum* valueOf(const QString&,
const QString&);
private:
QString _type;
QString _name;
int _ordinal;
//Constructor
public:
Enum(const QString&, const QString&);
virtual ~Enum(void);
public:
/** Get the Enum item's type. */
QString type() const {return _type;}
/** Get the Enum item's name. */
QString name() const {return _name;}
/** Get the Enum item's ordinal
(the integer index in it's type, zero based) */
int ordinal() const {return _ordinal;}
/** The represented string. */
virtual QString toString() const {return QString("[%1#%2]%3")
.arg(type()).arg(ordinal()).arg(name());}
private:
/** Forbidden copy constructor and assignment operator. */
Enum(const Enum&);
Enum& operator=(const Enum&);
};
/** Declare a new Enum type. */
#define DECLARE_ENUM(ENUM_TYPE)/
protected:/
ENUM_TYPE(const QString& ename) : Enum(QString(#ENUM_TYPE), ename) {}/
public:/
static QVector<const ENUM_TYPE *> values(){/
QVector<const ENUM_TYPE *> ret;/
foreach(const Enum * en, Enum::values(QString(#ENUM_TYPE)))/
{/
const ENUM_TYPE * ct = dynamic_cast<const ENUM_TYPE *>(en);/
if (ct) ret.push_back(ct);/
}/
return ret;/
}/
static const ENUM_TYPE* valueOf(const QString& ename){/
return dynamic_cast<const ENUM_TYPE *>(Enum::valueOf(QString(#ENUM_TYPE), ename));/
}
}
#include < QString >
#include < QMap >
#include < QVector >
#include < iosfwd >
namespace milk
{
class Enum
{
public:
/** Dump all Enum information out, only for debug. */
static void dump();
static void dump(std::ostream&);
protected:
/** A safe storage for store all enumerate items. */
static QMap<QString, QVector<const Enum*>>& EMAP();
/** Get all Enum items of the given Enum type. */
static QVector<const Enum*> values(const QString&);
/** Get the Enum items by it's type and name. */
static const Enum* valueOf(const QString&,
const QString&);
private:
QString _type;
QString _name;
int _ordinal;
//Constructor
public:
Enum(const QString&, const QString&);
virtual ~Enum(void);
public:
/** Get the Enum item's type. */
QString type() const {return _type;}
/** Get the Enum item's name. */
QString name() const {return _name;}
/** Get the Enum item's ordinal
(the integer index in it's type, zero based) */
int ordinal() const {return _ordinal;}
/** The represented string. */
virtual QString toString() const {return QString("[%1#%2]%3")
.arg(type()).arg(ordinal()).arg(name());}
private:
/** Forbidden copy constructor and assignment operator. */
Enum(const Enum&);
Enum& operator=(const Enum&);
};
/** Declare a new Enum type. */
#define DECLARE_ENUM(ENUM_TYPE)/
protected:/
ENUM_TYPE(const QString& ename) : Enum(QString(#ENUM_TYPE), ename) {}/
public:/
static QVector<const ENUM_TYPE *> values(){/
QVector<const ENUM_TYPE *> ret;/
foreach(const Enum * en, Enum::values(QString(#ENUM_TYPE)))/
{/
const ENUM_TYPE * ct = dynamic_cast<const ENUM_TYPE *>(en);/
if (ct) ret.push_back(ct);/
}/
return ret;/
}/
static const ENUM_TYPE* valueOf(const QString& ename){/
return dynamic_cast<const ENUM_TYPE *>(Enum::valueOf(QString(#ENUM_TYPE), ename));/
}
}
Enum.cpp
#include
<
cassert
>
#include < iostream >
#include " Enum.h "
namespace milk
{
Enum::Enum(const QString& type, const QString& name)
: _type(type), _name(name)
{
QMap<QString, QVector<const Enum*>>& emap = EMAP();
if (!emap.contains(type))
{
//Insert a new vector for the new Enum type
QVector<const Enum*> newEnums;
emap.insert(type, newEnums);
}
QVector<const Enum*>& enums = emap[type];
enums.append(this);
_ordinal = enums.size() - 1;
}
Enum::~Enum(void)
{
}
QMap<QString, QVector<const Enum*>>& Enum::EMAP()
{
static QMap<QString, QVector<const Enum*>> emap;
return emap;
}
QVector<const Enum*> Enum::values(const QString& type)
{
return EMAP()[type];
}
const Enum* Enum::valueOf(const QString& type, const QString& name)
{
foreach(const Enum* em, Enum::values(type))
{
if (em->name() == name)
return em;
}
return 0;
}
void Enum::dump(std::ostream& os)
{
QMap<QString, QVector<const Enum*>>& emap = EMAP();
foreach(QString key, emap.keys())
{
os<<" ";
foreach(const Enum* em, emap[key])
{
os<<qPrintable(em->toString())<<" ";
}
}
}
void Enum::dump()
{
QMap<QString, QVector<const Enum*>>& emap = EMAP();
foreach(QString key, emap.keys())
{
std::cout<<" ";
foreach(const Enum* em, emap[key])
{
std::cout<<qPrintable(em->toString())<<" ";
}
}
}
}
#include < iostream >
#include " Enum.h "
namespace milk
{
Enum::Enum(const QString& type, const QString& name)
: _type(type), _name(name)
{
QMap<QString, QVector<const Enum*>>& emap = EMAP();
if (!emap.contains(type))
{
//Insert a new vector for the new Enum type
QVector<const Enum*> newEnums;
emap.insert(type, newEnums);
}
QVector<const Enum*>& enums = emap[type];
enums.append(this);
_ordinal = enums.size() - 1;
}
Enum::~Enum(void)
{
}
QMap<QString, QVector<const Enum*>>& Enum::EMAP()
{
static QMap<QString, QVector<const Enum*>> emap;
return emap;
}
QVector<const Enum*> Enum::values(const QString& type)
{
return EMAP()[type];
}
const Enum* Enum::valueOf(const QString& type, const QString& name)
{
foreach(const Enum* em, Enum::values(type))
{
if (em->name() == name)
return em;
}
return 0;
}
void Enum::dump(std::ostream& os)
{
QMap<QString, QVector<const Enum*>>& emap = EMAP();
foreach(QString key, emap.keys())
{
os<<" ";
foreach(const Enum* em, emap[key])
{
os<<qPrintable(em->toString())<<" ";
}
}
}
void Enum::dump()
{
QMap<QString, QVector<const Enum*>>& emap = EMAP();
foreach(QString key, emap.keys())
{
std::cout<<" ";
foreach(const Enum* em, emap[key])
{
std::cout<<qPrintable(em->toString())<<" ";
}
}
}
}
头文件里面定义了一个 DECLARE_ENUM宏,用来为从Enum类派生的枚举类型生成一个构造函数和values 与valueOf 函数。
因为不能像Java一样由编译器自动生成辅助代码,所以使用上(枚举类型和枚举量的定义)会比Java要复杂一些,不过相对而言,也比较灵活一些。
下面是定义一个枚举类型的示例:
#pragma
once
#include < QImage >
#include " ../Core/Enum.h "
#include " AnalysisResult.h "
namespace milk
{
class AnalysisInput;
class IQAnalyser : public Enum
{
DECLARE_ENUM(IQAnalyser)
public:
/** Analysis method. */
SP_AnalysisResult analysis(const AnalysisInput& input) const
{
return analysisInt(input);
}
/** Export result method. */
void exportResult(SP_AnalysisResult result, const QString& exFile) const
{
return exportResultInt(result, exFile);
}
QString chartType() const {return chartTypeInt();}
private:
virtual SP_AnalysisResult
analysisInt(const AnalysisInput&) const = 0;
virtual void
exportResultInt(SP_AnalysisResult, const QString&) const = 0;
virtual QString chartTypeInt() const = 0;
private:
IQAnalyser(const IQAnalyser&);
IQAnalyser& operator=(const IQAnalyser&);
};
}
#include < QImage >
#include " ../Core/Enum.h "
#include " AnalysisResult.h "
namespace milk
{
class AnalysisInput;
class IQAnalyser : public Enum
{
DECLARE_ENUM(IQAnalyser)
public:
/** Analysis method. */
SP_AnalysisResult analysis(const AnalysisInput& input) const
{
return analysisInt(input);
}
/** Export result method. */
void exportResult(SP_AnalysisResult result, const QString& exFile) const
{
return exportResultInt(result, exFile);
}
QString chartType() const {return chartTypeInt();}
private:
virtual SP_AnalysisResult
analysisInt(const AnalysisInput&) const = 0;
virtual void
exportResultInt(SP_AnalysisResult, const QString&) const = 0;
virtual QString chartTypeInt() const = 0;
private:
IQAnalyser(const IQAnalyser&);
IQAnalyser& operator=(const IQAnalyser&);
};
}
接下来我们在实际使用中就可以为这个枚举类型派生出多个子类型,并为每个子类型生成一个单件常量(枚举量),类似这样:
ISO13660Analyser.h
#pragma
once
#include < PureMilk / Analyser / IQAnalyser.h >
#include < QString >
namespace milk
{
class IS013660Analyser : public IQAnalyser
{
private:
IS013660Analyser(const QString& name): IQAnalyser(name){}
virtual SP_AnalysisResult
analysisInt(const AnalysisInput&) const;
virtual void
exportResultInt(SP_AnalysisResult, const QString&) const;
virtual QString chartTypeInt() const
{
return QString("ISO-13660");
}
public:
static const IQAnalyser* ISO13660_ANALYSER;
};
}
#include < PureMilk / Analyser / IQAnalyser.h >
#include < QString >
namespace milk
{
class IS013660Analyser : public IQAnalyser
{
private:
IS013660Analyser(const QString& name): IQAnalyser(name){}
virtual SP_AnalysisResult
analysisInt(const AnalysisInput&) const;
virtual void
exportResultInt(SP_AnalysisResult, const QString&) const;
virtual QString chartTypeInt() const
{
return QString("ISO-13660");
}
public:
static const IQAnalyser* ISO13660_ANALYSER;
};
}
#include
"
IS013660Analyser.h
"
namespace milk
{
const IQAnalyser* IS013660Analyser::ISO13660_ANALYSER =
new IS013660Analyser("ISO-13660 Analyser");
... ... ... ...
}
namespace milk
{
const IQAnalyser* IS013660Analyser::ISO13660_ANALYSER =
new IS013660Analyser("ISO-13660 Analyser");
... ... ... ...
}
如果需要访问某个特定的枚举量,子类型的单件声明也可以放在父类型里,像Java的实现一样,不过并没有Java那样必须如此的限制。
以上的实现在UI界面的呈现上会有巨大的便利,我们可以通过枚举类型的values调用获得整个枚举量的集合,把它们的名字放在列表框或者下拉选框中让用户选择。然后把用户的选择使用多态自动派遣到具体的实现逻辑中去。