具有 32-bit/64-bit 整数重载的模板函数
Template function with 32-bit/64-bit integer overloads
实际函数 bar
应该是从文件中读取数据,其中写入的数据正好是 4 个字节或 8 个字节(unsigned int
- DWORD
或 DWORD64
)
void bar(DWORD64&);
void bar(DWORD&);
template<typename IntType>
void foo(IntType& out)
{
bar(out);
}
int main()
{
int a;
foo(a); // Caller doesn't care
}
因为调用者可以传递任何整数类型(int
、LONG
、DWORD
、LONGLONG
或任何类型)——我想要一种技术,以便 foo
可以调用 32 位 bar
或 64 位 bar
.
简而言之,就像:
template<typename IntType>
void foo(IntType& out)
{
if(sizeof(out)==sizeof(DWORD)) // 32-bit
{
DWORD data;
bar(data); // call DWORD version
out = (IntType)data; // Ignore truncation etc.
}
else
{
DWORD64 data;
bar(data); // call DWORD64 version
out = (IntType)data; // Ignore truncation etc.
}
}
显然,我希望在编译时解析“if”部分。 std::enable_if
之类的?
您可以使用std::conditional
选择类型:
template<typename IntType>
void foo(IntType& out){
using dword_t = typename std::conditional<sizeof(IntType) == sizeof(DWORD), DWORD, DWORD64>::type;
dword_t data;
bar(data);
out = (IntType)data;
}
你可以使用 C++17 constexpr if
,但我不确定这种方法是否真的更具可读性:
template<typename IntType>
void foo(IntType& out)
{
if constexpr(sizeof(out)==sizeof(DWORD)) // 32-bit
{
DWORD data;
bar(data); // call DWORD version
out = (IntType)data; // Ignore truncation etc.
}
else
{
DWORD64 data;
bar(data); // call DWORD64 version
out = (IntType)data; // Ignore truncation etc.
}
}
您可以使用 std::conditional
来仅区分 sizeof(DWORD)
和 sizeof(DWORD64)
(因为您想要支持的不仅仅是这两种类型):
template<typename IntType>
void foo(IntType& out)
{
typedef typename std::conditional<sizeof(IntType) == sizeof(DWORD), DWORD, DWORD64>::type RetType;
RetType data;
bar(data);
out = static_cast<IntType>(data);
}
问题是,如果你传递了一个int16_t
,你是想符号扩展、补零还是错误?
IMO 正确的做法是错误,所以这里有一个模板专业化的解决方案:
template <typename IntType>
void foo ( IntType * out ) = delete;
template <>
void foo ( uint64_t * out ) { bar ( * (DWORD64 *) out ); }
template <>
void foo ( int64_t * out ) { bar ( * (DWORD64 *) out ); }
template <>
void foo ( uint32_t * out ) { bar ( * (DWORD *) out ); }
template <>
void foo ( int32_t * out ) { bar ( * (DWORD *) out ); }
解决方案 1:SFINAE 和 std::enable_if
:
template<typename IntType, typename std::enable_if<sizeof(IntType) == 4>::type* = nullptr>
void foo(IntType& out)
{
DWORD arg = out;
bar(arg);
out = arg;
}
template<typename IntType, typename std::enable_if<sizeof(IntType) == 8>::type* = nullptr>
void foo(IntType& out)
{
DWORD64 arg = out;
bar(arg);
out = arg;
}
解决方案 2:委托给 class 和部分专业化:
template<typename IntType>
void foo(IntType& out)
{
foo_helper<IntType>::call(out);
}
template <class IntType, std::size_t Size = sizeof(IntType)>
struct foo_helper;
template <class IntType>
struct foo_helper<IntType, 4>
{
static void call(IntType &out)
{
DWORD arg = out;
bar(arg);
out = arg;
}
};
template <class IntType>
struct foo_helper<IntType, 8>
{
static void call(IntType &out)
{
DWORD64 arg = out;
bar(arg);
out = arg;
}
};
两种解决方案都可以添加 static_cast
调味,特别是在作业 to/from arg
.
附近
实际函数 bar
应该是从文件中读取数据,其中写入的数据正好是 4 个字节或 8 个字节(unsigned int
- DWORD
或 DWORD64
)
void bar(DWORD64&);
void bar(DWORD&);
template<typename IntType>
void foo(IntType& out)
{
bar(out);
}
int main()
{
int a;
foo(a); // Caller doesn't care
}
因为调用者可以传递任何整数类型(int
、LONG
、DWORD
、LONGLONG
或任何类型)——我想要一种技术,以便 foo
可以调用 32 位 bar
或 64 位 bar
.
简而言之,就像:
template<typename IntType>
void foo(IntType& out)
{
if(sizeof(out)==sizeof(DWORD)) // 32-bit
{
DWORD data;
bar(data); // call DWORD version
out = (IntType)data; // Ignore truncation etc.
}
else
{
DWORD64 data;
bar(data); // call DWORD64 version
out = (IntType)data; // Ignore truncation etc.
}
}
显然,我希望在编译时解析“if”部分。 std::enable_if
之类的?
您可以使用std::conditional
选择类型:
template<typename IntType>
void foo(IntType& out){
using dword_t = typename std::conditional<sizeof(IntType) == sizeof(DWORD), DWORD, DWORD64>::type;
dword_t data;
bar(data);
out = (IntType)data;
}
你可以使用 C++17 constexpr if
,但我不确定这种方法是否真的更具可读性:
template<typename IntType>
void foo(IntType& out)
{
if constexpr(sizeof(out)==sizeof(DWORD)) // 32-bit
{
DWORD data;
bar(data); // call DWORD version
out = (IntType)data; // Ignore truncation etc.
}
else
{
DWORD64 data;
bar(data); // call DWORD64 version
out = (IntType)data; // Ignore truncation etc.
}
}
您可以使用 std::conditional
来仅区分 sizeof(DWORD)
和 sizeof(DWORD64)
(因为您想要支持的不仅仅是这两种类型):
template<typename IntType>
void foo(IntType& out)
{
typedef typename std::conditional<sizeof(IntType) == sizeof(DWORD), DWORD, DWORD64>::type RetType;
RetType data;
bar(data);
out = static_cast<IntType>(data);
}
问题是,如果你传递了一个int16_t
,你是想符号扩展、补零还是错误?
IMO 正确的做法是错误,所以这里有一个模板专业化的解决方案:
template <typename IntType>
void foo ( IntType * out ) = delete;
template <>
void foo ( uint64_t * out ) { bar ( * (DWORD64 *) out ); }
template <>
void foo ( int64_t * out ) { bar ( * (DWORD64 *) out ); }
template <>
void foo ( uint32_t * out ) { bar ( * (DWORD *) out ); }
template <>
void foo ( int32_t * out ) { bar ( * (DWORD *) out ); }
解决方案 1:SFINAE 和 std::enable_if
:
template<typename IntType, typename std::enable_if<sizeof(IntType) == 4>::type* = nullptr>
void foo(IntType& out)
{
DWORD arg = out;
bar(arg);
out = arg;
}
template<typename IntType, typename std::enable_if<sizeof(IntType) == 8>::type* = nullptr>
void foo(IntType& out)
{
DWORD64 arg = out;
bar(arg);
out = arg;
}
解决方案 2:委托给 class 和部分专业化:
template<typename IntType>
void foo(IntType& out)
{
foo_helper<IntType>::call(out);
}
template <class IntType, std::size_t Size = sizeof(IntType)>
struct foo_helper;
template <class IntType>
struct foo_helper<IntType, 4>
{
static void call(IntType &out)
{
DWORD arg = out;
bar(arg);
out = arg;
}
};
template <class IntType>
struct foo_helper<IntType, 8>
{
static void call(IntType &out)
{
DWORD64 arg = out;
bar(arg);
out = arg;
}
};
两种解决方案都可以添加 static_cast
调味,特别是在作业 to/from arg
.