预备知识
宏定义__STDC_FORMAT_MACROS
- 64位整数,在32位系统中是long long int,在64位系统中是long int
- 64位整数printf打印,在32位系统中是lld,在64位系统中是ld
- 跨平台打印方案,
printf("%" PRId64 "\n", value) \\long int value(64位) long long int value(32位)
,PRId64在inttypes.h中定义 - 这个设计是给c用的,c++定义一个__STDC_FORMAT_MACROS宏显示打开它
宏定义__attribute__((visibility("default")))
https://blog.csdn.net/mutourenzhang/article/details/47803803
- 用于控制链接库总函数对外是否可见
- windows下
#define GFLAGS_DLL_DECLARE_FLAG __declspec(dllimport)
预处理结果查看
- gcc -E只预处理不编译
利用宏和模板存储值类型
- 假设类
value_buffer_
字段可以存储值;type_
字段存储值对应bool,int,string等类型,如何实现 - 自己经常用的实现方式(构造时显示传入值类型,只有一个构造函数)
#include <iostream>
#include <string>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdint.h>
#include <inttypes.h>
using std::cout;
using std::string;
typedef signed char int8;
typedef unsigned char uint8;
typedef int32_t int32;
typedef uint32_t uint32;
typedef int64_t int64;
typedef uint64_t uint64;
typedef std::string clstring;
enum ValueType {
FV_BOOL = 0,
FV_INT32 = 1,
FV_UINT32 = 2,
FV_INT64 = 3,
FV_UINT64 = 4,
FV_DOUBLE = 5,
FV_STRING = 6,
FV_MAX_INDEX = 6,
};
#define strto64 strtoll
#define strtou64 strtoull
#define VALUE_AS(type) *reinterpret_cast<type *>(value_buffer_)
#define OTHER_VALUE_AS(fv, type) *reinterpret_cast<type *>(fv.value_buffer_)
#define SET_VALUE_AS(type, value) VALUE_AS(type) = (value)
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
class FlagValue {
public:
FlagValue(void *const valbuf, int8 type, bool transfer_ownership_of_value)
: value_buffer_(valbuf), type_(type),
owns_value_(transfer_ownership_of_value) {}
~FlagValue() {
if (!owns_value_) {
return;
}
switch (type_) {
case FV_BOOL:
delete reinterpret_cast<bool *>(value_buffer_);
break;
case FV_INT32:
delete reinterpret_cast<int32 *>(value_buffer_);
break;
case FV_UINT32:
delete reinterpret_cast<uint32 *>(value_buffer_);
break;
case FV_INT64:
delete reinterpret_cast<int64 *>(value_buffer_);
break;
case FV_UINT64:
delete reinterpret_cast<uint64 *>(value_buffer_);
break;
case FV_DOUBLE:
delete reinterpret_cast<double *>(value_buffer_);
break;
case FV_STRING:
delete reinterpret_cast<string *>(value_buffer_);
break;
}
}
bool ParseFrom(const char *value) {
if (type_ == FV_BOOL) {
const char *kTrue[] = {"1", "t", "true", "y", "yes"};
const char *kFalse[] = {"0", "f", "false", "n", "no"};
for (size_t i = 0; i < sizeof(kTrue) / sizeof(*kTrue); ++i) {
if (strcasecmp(value, kTrue[i]) == 0) {
SET_VALUE_AS(bool, true);
return true;
} else if (strcasecmp(value, kFalse[i]) == 0) {
SET_VALUE_AS(bool, false);
return true;
}
}
return false; // didn't match a legal input
} else if (type_ == FV_STRING) {
SET_VALUE_AS(string, value);
return true;
}
// OK, it's likely to be numeric, and we'll be using a strtoXXX method.
if (value[0] == '\0') // empty-string is only allowed for string type.
return false;
char *end;
// Leading 0x puts us in base 16. But leading 0 does not put us in base 8!
// It caused too many bugs when we had that behavior.
int base = 10; // by default
if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X'))
base = 16;
errno = 0;
switch (type_) {
case FV_INT32: {
const int64 r = strto64(value, &end, base);
if (errno || end != value + strlen(value))
return false; // bad parse
if (static_cast<int32>(r) != r) // worked, but number out of range
return false;
SET_VALUE_AS(int32, static_cast<int32>(r));
return true;
}
case FV_UINT32: {
while (*value == ' ')
value++;
if (*value == '-')
return false; // negative number
const uint64 r = strtou64(value, &end, base);
if (errno || end != value + strlen(value))
return false; // bad parse
if (static_cast<uint32>(r) != r) // worked, but number out of range
return false;
SET_VALUE_AS(uint32, static_cast<uint32>(r));
return true;
}
case FV_INT64: {
const int64 r = strto64(value, &end, base);
if (errno || end != value + strlen(value))
return false; // bad parse
SET_VALUE_AS(int64, r);
return true;
}
case FV_UINT64: {
while (*value == ' ')
value++;
if (*value == '-')
return false; // negative number
const uint64 r = strtou64(value, &end, base);
if (errno || end != value + strlen(value))
return false; // bad parse
SET_VALUE_AS(uint64, r);
return true;
}
case FV_DOUBLE: {
const double r = strtod(value, &end);
if (errno || end != value + strlen(value))
return false; // bad parse
SET_VALUE_AS(double, r);
return true;
}
default: {
assert(false); // unknown type
return false;
}
}
}
string ToString() const {
char intbuf[64]; // enough to hold even the biggest number
switch (type_) {
case FV_BOOL:
return VALUE_AS(bool) ? "true" : "false";
case FV_INT32:
snprintf(intbuf, sizeof(intbuf), "%" PRId32, VALUE_AS(int32));
return intbuf;
case FV_UINT32:
snprintf(intbuf, sizeof(intbuf), "%" PRIu32, VALUE_AS(uint32));
return intbuf;
case FV_INT64:
snprintf(intbuf, sizeof(intbuf), "%" PRId64, VALUE_AS(int64));
return intbuf;
case FV_UINT64:
snprintf(intbuf, sizeof(intbuf), "%" PRIu64, VALUE_AS(uint64));
return intbuf;
case FV_DOUBLE:
snprintf(intbuf, sizeof(intbuf), "%.17g", VALUE_AS(double));
return intbuf;
case FV_STRING:
return VALUE_AS(string);
default:
assert(false);
return ""; // unknown type
}
}
ValueType Type() const { return static_cast<ValueType>(type_); }
private:
void *const value_buffer_; // points to the buffer holding our data
const int8 type_; // how to interpret value_
const bool owns_value_; // whether to free value on destruct
};
int main(int argc, char **argv) {
int val1 = 30;
FlagValue value1(&val1, FV_INT32, false);
std::cout << value1.ToString() << std::endl;
string val2 = "test!!!";
FlagValue value2(&val2, FV_STRING, false);
std::cout << value2.ToString() << std::endl;
return 0;
}
- 看Google的gflags源码种利用构造函数模板实现
#include <iostream>
#include <string>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdint.h>
#include <inttypes.h>
using std::cout;
using std::string;
typedef signed char int8;
typedef unsigned char uint8;
typedef int32_t int32;
typedef uint32_t uint32;
typedef int64_t int64;
typedef uint64_t uint64;
typedef std::string clstring;
enum ValueType {
FV_BOOL = 0,
FV_INT32 = 1,
FV_UINT32 = 2,
FV_INT64 = 3,
FV_UINT64 = 4,
FV_DOUBLE = 5,
FV_STRING = 6,
FV_MAX_INDEX = 6,
};
#define strto64 strtoll
#define strtou64 strtoull
#define VALUE_AS(type) *reinterpret_cast<type *>(value_buffer_)
#define OTHER_VALUE_AS(fv, type) *reinterpret_cast<type *>(fv.value_buffer_)
#define SET_VALUE_AS(type, value) VALUE_AS(type) = (value)
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
template <typename FlagType> struct FlagValueTraits;
// Map the given C++ type to a value of the ValueType enum at compile time.
#define DEFINE_FLAG_TRAITS(type, value) \
template <> struct FlagValueTraits<type> { \
static const ValueType kValueType = value; \
}
DEFINE_FLAG_TRAITS(bool, ValueType::FV_BOOL);
DEFINE_FLAG_TRAITS(int32, ValueType::FV_INT32);
DEFINE_FLAG_TRAITS(uint32, ValueType::FV_UINT32);
DEFINE_FLAG_TRAITS(int64, ValueType::FV_INT64);
DEFINE_FLAG_TRAITS(uint64, ValueType::FV_UINT64);
DEFINE_FLAG_TRAITS(double, ValueType::FV_DOUBLE);
DEFINE_FLAG_TRAITS(std::string, ValueType::FV_STRING);
#undef DEFINE_FLAG_TRAITS
class FlagValue {
public:
template <typename FlagType>
FlagValue(FlagType *valbuf, bool transfer_ownership_of_value)
: value_buffer_(valbuf), type_(FlagValueTraits<FlagType>::kValueType),
owns_value_(transfer_ownership_of_value) {}
~FlagValue() {
if (!owns_value_) {
return;
}
switch (type_) {
case FV_BOOL:
delete reinterpret_cast<bool *>(value_buffer_);
break;
case FV_INT32:
delete reinterpret_cast<int32 *>(value_buffer_);
break;
case FV_UINT32:
delete reinterpret_cast<uint32 *>(value_buffer_);
break;
case FV_INT64:
delete reinterpret_cast<int64 *>(value_buffer_);
break;
case FV_UINT64:
delete reinterpret_cast<uint64 *>(value_buffer_);
break;
case FV_DOUBLE:
delete reinterpret_cast<double *>(value_buffer_);
break;
case FV_STRING:
delete reinterpret_cast<string *>(value_buffer_);
break;
}
}
bool ParseFrom(const char *value) {
if (type_ == FV_BOOL) {
const char *kTrue[] = {"1", "t", "true", "y", "yes"};
const char *kFalse[] = {"0", "f", "false", "n", "no"};
for (size_t i = 0; i < sizeof(kTrue) / sizeof(*kTrue); ++i) {
if (strcasecmp(value, kTrue[i]) == 0) {
SET_VALUE_AS(bool, true);
return true;
} else if (strcasecmp(value, kFalse[i]) == 0) {
SET_VALUE_AS(bool, false);
return true;
}
}
return false; // didn't match a legal input
} else if (type_ == FV_STRING) {
SET_VALUE_AS(string, value);
return true;
}
// OK, it's likely to be numeric, and we'll be using a strtoXXX method.
if (value[0] == '\0') // empty-string is only allowed for string type.
return false;
char *end;
// Leading 0x puts us in base 16. But leading 0 does not put us in base 8!
// It caused too many bugs when we had that behavior.
int base = 10; // by default
if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X'))
base = 16;
errno = 0;
switch (type_) {
case FV_INT32: {
const int64 r = strto64(value, &end, base);
if (errno || end != value + strlen(value))
return false; // bad parse
if (static_cast<int32>(r) != r) // worked, but number out of range
return false;
SET_VALUE_AS(int32, static_cast<int32>(r));
return true;
}
case FV_UINT32: {
while (*value == ' ')
value++;
if (*value == '-')
return false; // negative number
const uint64 r = strtou64(value, &end, base);
if (errno || end != value + strlen(value))
return false; // bad parse
if (static_cast<uint32>(r) != r) // worked, but number out of range
return false;
SET_VALUE_AS(uint32, static_cast<uint32>(r));
return true;
}
case FV_INT64: {
const int64 r = strto64(value, &end, base);
if (errno || end != value + strlen(value))
return false; // bad parse
SET_VALUE_AS(int64, r);
return true;
}
case FV_UINT64: {
while (*value == ' ')
value++;
if (*value == '-')
return false; // negative number
const uint64 r = strtou64(value, &end, base);
if (errno || end != value + strlen(value))
return false; // bad parse
SET_VALUE_AS(uint64, r);
return true;
}
case FV_DOUBLE: {
const double r = strtod(value, &end);
if (errno || end != value + strlen(value))
return false; // bad parse
SET_VALUE_AS(double, r);
return true;
}
default: {
assert(false); // unknown type
return false;
}
}
}
string ToString() const {
char intbuf[64]; // enough to hold even the biggest number
switch (type_) {
case FV_BOOL:
return VALUE_AS(bool) ? "true" : "false";
case FV_INT32:
snprintf(intbuf, sizeof(intbuf), "%" PRId32, VALUE_AS(int32));
return intbuf;
case FV_UINT32:
snprintf(intbuf, sizeof(intbuf), "%" PRIu32, VALUE_AS(uint32));
return intbuf;
case FV_INT64:
snprintf(intbuf, sizeof(intbuf), "%" PRId64, VALUE_AS(int64));
return intbuf;
case FV_UINT64:
snprintf(intbuf, sizeof(intbuf), "%" PRIu64, VALUE_AS(uint64));
return intbuf;
case FV_DOUBLE:
snprintf(intbuf, sizeof(intbuf), "%.17g", VALUE_AS(double));
return intbuf;
case FV_STRING:
return VALUE_AS(string);
default:
assert(false);
return ""; // unknown type
}
}
ValueType Type() const { return static_cast<ValueType>(type_); }
private:
void *const value_buffer_; // points to the buffer holding our data
const int8 type_; // how to interpret value_
const bool owns_value_; // whether to free value on destruct
};
int main(int argc, char **argv) {
int val1 = 30;
FlagValue value1(&val1, false);
std::cout << value1.ToString() << std::endl;
string val2 = "test!!!";
FlagValue value2(&val2, false);
std::cout << value2.ToString() << std::endl;
return 0;
}
- 构造了不同类型的模板构造函数(看编译器对模板的实现,是不是多个构造函数重载,不误导读者),且构造函数参数根据类型传入不同类型FlagValueTraits结构体,根据结构体kValueType值映射类型,和前一种实现显示传入类型并无本质区别
- Google这种实现好处是什么?
- 为了更好理解代码,只预编译源代码如下
//g++ main.cc -E -g -o main
//已经去掉了标准库的预处理替换(2万多行)
......
using std::cout;
using std::string;
typedef signed char int8;
typedef unsigned char uint8;
typedef int32_t int32;
typedef uint32_t uint32;
typedef int64_t int64;
typedef uint64_t uint64;
typedef std::string clstring;
enum ValueType {
FV_BOOL = 0,
FV_INT32 = 1,
FV_UINT32 = 2,
FV_INT64 = 3,
FV_UINT64 = 4,
FV_DOUBLE = 5,
FV_STRING = 6,
FV_MAX_INDEX = 6,
};
template <typename FlagType> struct FlagValueTraits;
template <> struct FlagValueTraits<bool> {
static const ValueType kValueType = ValueType::FV_BOOL;
};
template <> struct FlagValueTraits<int32> {
static const ValueType kValueType = ValueType::FV_INT32;
};
template <> struct FlagValueTraits<uint32> {
static const ValueType kValueType = ValueType::FV_UINT32;
};
template <> struct FlagValueTraits<int64> {
static const ValueType kValueType = ValueType::FV_INT64;
};
template <> struct FlagValueTraits<uint64> {
static const ValueType kValueType = ValueType::FV_UINT64;
};
template <> struct FlagValueTraits<double> {
static const ValueType kValueType = ValueType::FV_DOUBLE;
};
template <> struct FlagValueTraits<std::string> {
static const ValueType kValueType = ValueType::FV_STRING;
};
class FlagValue {
public:
template <typename FlagType>
FlagValue(FlagType *valbuf, bool transfer_ownership_of_value)
: value_buffer_(valbuf), type_(FlagValueTraits<FlagType>::kValueType),
owns_value_(transfer_ownership_of_value) {}
~FlagValue() {
if (!owns_value_) {
return;
}
switch (type_) {
case FV_BOOL:
delete reinterpret_cast<bool *>(value_buffer_);
break;
case FV_INT32:
delete reinterpret_cast<int32 *>(value_buffer_);
break;
case FV_UINT32:
delete reinterpret_cast<uint32 *>(value_buffer_);
break;
case FV_INT64:
delete reinterpret_cast<int64 *>(value_buffer_);
break;
case FV_UINT64:
delete reinterpret_cast<uint64 *>(value_buffer_);
break;
case FV_DOUBLE:
delete reinterpret_cast<double *>(value_buffer_);
break;
case FV_STRING:
delete reinterpret_cast<string *>(value_buffer_);
break;
}
}
bool ParseFrom(const char *value) {
if (type_ == FV_BOOL) {
const char *kTrue[] = {"1", "t", "true", "y", "yes"};
const char *kFalse[] = {"0", "f", "false", "n", "no"};
for (size_t i = 0; i < sizeof(kTrue) / sizeof(*kTrue); ++i) {
if (strcasecmp(value, kTrue[i]) == 0) {
*reinterpret_cast<bool *>(value_buffer_) = (true);
return true;
} else if (strcasecmp(value, kFalse[i]) == 0) {
*reinterpret_cast<bool *>(value_buffer_) = (false);
return true;
}
}
return false;
} else if (type_ == FV_STRING) {
*reinterpret_cast<string *>(value_buffer_) = (value);
return true;
}
if (value[0] == '\0')
return false;
char *end;
int base = 10;
if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X'))
base = 16;
# 125 "/home/niu/cplusstudy/aa.cc" 3 4
(*__errno_location())
# 125 "/home/niu/cplusstudy/aa.cc"
= 0;
switch (type_) {
case FV_INT32: {
const int64 r = strtoll(value, &end, base);
if (
# 130 "/home/niu/cplusstudy/aa.cc" 3 4
(*__errno_location())
# 130 "/home/niu/cplusstudy/aa.cc"
|| end != value + strlen(value))
return false;
if (static_cast<int32>(r) != r)
return false;
*reinterpret_cast<int32 *>(value_buffer_) = (static_cast<int32>(r));
return true;
}
case FV_UINT32: {
while (*value == ' ')
value++;
if (*value == '-')
return false;
const uint64 r = strtoull(value, &end, base);
if (
# 143 "/home/niu/cplusstudy/aa.cc" 3 4
(*__errno_location())
# 143 "/home/niu/cplusstudy/aa.cc"
|| end != value + strlen(value))
return false;
if (static_cast<uint32>(r) != r)
return false;
*reinterpret_cast<uint32 *>(value_buffer_) = (static_cast<uint32>(r));
return true;
}
case FV_INT64: {
const int64 r = strtoll(value, &end, base);
if (
# 152 "/home/niu/cplusstudy/aa.cc" 3 4
(*__errno_location())
# 152 "/home/niu/cplusstudy/aa.cc"
|| end != value + strlen(value))
return false;
*reinterpret_cast<int64 *>(value_buffer_) = (r);
return true;
}
case FV_UINT64: {
while (*value == ' ')
value++;
if (*value == '-')
return false;
const uint64 r = strtoull(value, &end, base);
if (
# 163 "/home/niu/cplusstudy/aa.cc" 3 4
(*__errno_location())
# 163 "/home/niu/cplusstudy/aa.cc"
|| end != value + strlen(value))
return false;
*reinterpret_cast<uint64 *>(value_buffer_) = (r);
return true;
}
case FV_DOUBLE: {
const double r = strtod(value, &end);
if (
# 170 "/home/niu/cplusstudy/aa.cc" 3 4
(*__errno_location())
# 170 "/home/niu/cplusstudy/aa.cc"
|| end != value + strlen(value))
return false;
*reinterpret_cast<double *>(value_buffer_) = (r);
return true;
}
default: {
# 176 "/home/niu/cplusstudy/aa.cc" 3 4
((
# 176 "/home/niu/cplusstudy/aa.cc"
false
# 176 "/home/niu/cplusstudy/aa.cc" 3 4
)
? static_cast<void>(0)
: __assert_fail(
# 176 "/home/niu/cplusstudy/aa.cc"
"false"
# 176 "/home/niu/cplusstudy/aa.cc" 3 4
,
"/home/niu/cplusstudy/aa.cc", 176, __PRETTY_FUNCTION__))
# 176 "/home/niu/cplusstudy/aa.cc"
;
return false;
}
}
}
string ToString() const {
char intbuf[64];
switch (type_) {
case FV_BOOL:
return *reinterpret_cast<bool *>(value_buffer_) ? "true" : "false";
case FV_INT32:
snprintf(intbuf, sizeof(intbuf),
"%"
# 188 "/home/niu/cplusstudy/aa.cc" 3 4
"d"
# 188 "/home/niu/cplusstudy/aa.cc"
,
*reinterpret_cast<int32 *>(value_buffer_));
return intbuf;
case FV_UINT32:
snprintf(intbuf, sizeof(intbuf),
"%"
# 191 "/home/niu/cplusstudy/aa.cc" 3 4
"u"
# 191 "/home/niu/cplusstudy/aa.cc"
,
*reinterpret_cast<uint32 *>(value_buffer_));
return intbuf;
case FV_INT64:
snprintf(intbuf, sizeof(intbuf),
"%"
# 194 "/home/niu/cplusstudy/aa.cc" 3 4
"l"
"d"
# 194 "/home/niu/cplusstudy/aa.cc"
,
*reinterpret_cast<int64 *>(value_buffer_));
return intbuf;
case FV_UINT64:
snprintf(intbuf, sizeof(intbuf),
"%"
# 197 "/home/niu/cplusstudy/aa.cc" 3 4
"l"
"u"
# 197 "/home/niu/cplusstudy/aa.cc"
,
*reinterpret_cast<uint64 *>(value_buffer_));
return intbuf;
case FV_DOUBLE:
snprintf(intbuf, sizeof(intbuf), "%.17g",
*reinterpret_cast<double *>(value_buffer_));
return intbuf;
case FV_STRING:
return *reinterpret_cast<string *>(value_buffer_);
default:
# 205 "/home/niu/cplusstudy/aa.cc" 3 4
((
# 205 "/home/niu/cplusstudy/aa.cc"
false
# 205 "/home/niu/cplusstudy/aa.cc" 3 4
)
? static_cast<void>(0)
: __assert_fail(
# 205 "/home/niu/cplusstudy/aa.cc"
"false"
# 205 "/home/niu/cplusstudy/aa.cc" 3 4
,
"/home/niu/cplusstudy/aa.cc", 205, __PRETTY_FUNCTION__))
# 205 "/home/niu/cplusstudy/aa.cc"
;
return "";
}
}
ValueType Type() const { return static_cast<ValueType>(type_); }
private:
void *const value_buffer_;
const int8 type_;
const bool owns_value_;
};
int main(int argc, char **argv) {
int val1 = 30;
FlagValue value1(&val1, false);
std::cout << value1.ToString() << std::endl;
string val2 = "test!!!";
FlagValue value2(&val2, false);
std::cout << value2.ToString() << std::endl;
return 0;
}