c++ - 将非模板类的成员函数转换为函数模板
问题描述
我正在研究一个类的成员函数。以前版本中的类如下所示:
#include "cstr.h" // contains all of the needed function templates, and these work appropriately
// to_string<T>(T val) - signed and unsigned types non hex mode
// to_hstring<T>(T val, char hex_mode) - unsigned types hex mode
// to_string<T>(T val, uint8_t decimal_places) - floating point types
class Foo {
public:
Print(const char* str) {
// implementation here
}
Print(char c) { PutChar(c); }
// Unsigned Integer Types
Print(uint64_t val) { Print(to_string<uint64_t>(val); }
Print(uint32_t val) { Print(to_string<uint32_t>(val); }
Print(uint16_t val) { Print(to_string<uint16_t>(val); }
Print(uint8_t val) { Print(to_string<uint8_t>(val); }
// Signed Integer Types
Print(int64_t val) { Print(to_string<int64_t>(val); }
Print(int32_t val) { Print(to_string<int32_t>(val); }
Print(int16_t val) { Print(to_string<int16_t>(val); }
Print(int8_t val) { Print(to_string<int8_t>(val); }
// Unsigned Integer Types - HexFormat
Print(uint64_t val, char hex_mode) { Print(to_hstring<uint64_t>(val); }
Print(uint32_t val, char hex_mode) { Print(to_hstring<uint32_t>(val); }
Print(uint16_t val, char hex_mode) { Print(to_hstring<uint16_t>(val); }
Print(uint8_t val, char hex_mode) { Print(to_hstring<uint8_t>(val); }
// Floating Point Types
Print(double val, uint8_t decimal_places) { Print(to_string<double>(val, decimal_places); }
Print(float val, uint8_t decimal_places) { Print(to_string<float>(val, decimal_places); }
};
我目前正在像这样使用它们:
void Bar {
Foo foo(/*needed params for construction */);
foo.Print("This is a test string\n"); // Prints: This is a test string, and moves cursor down to a newline
foo.Print((uint64_t)12345);
foo.Print( '\n' ); // moves cursor down to a newline
foo.Print((double)1234.56789, 3); // Prints 1234.567
foo.Print( '\n' );
foo.Print((uint32_t)598732, 'h' ); // Converts 598732 to hex and Prints its value
foo.Print( '\n' );
foo.Print((float)23.4587f, 2); // Prints 23.45f
};
正如你从上面看到的......这将是使用函数模板重构这段代码的好时机......
我知道你不能用partial template specialization
with function templates
。
我希望能够将这些函数转换为模板......我假设所需的声明是:
template<typename T>
void Print(T val );
template<typename T>
void Print(T val, char hex_mode);
template<typename T>
void Print(T val, uint8_t decimal_places);
但是,当我尝试将这些函数转换为模板时,我无法让它们正确编译或链接......我尝试了很多不同的东西来在这里列出所有这些......我'我已经尝试在类主体之外定义它们,内联它们,我已经尝试在它的Foo.cpp
文件中定义它们,等等......
我希望这些通过自动类型扣除来解决......
不同类型有特殊情况...
如果T = const char*
那么这是将字符串打印到屏幕的实际实现。如果T = char
它调用PutChar
在屏幕上打印单个字符或换行符。这些我会假设决心使用...
template<typename T>
void Print(T val);
如果T = Signed or Unsigned Integer Type
. 他们应该调用Print(to_string<T>(val));
并且不能与T=char
or混淆T=const char*
。另外,解决..
template<typename T>
void Print(T val);
T
不能double
或float
在上述情况下...见下文。
如果T = unsigned
AND 存在重载版本char hex_mode
,那么它应该解析为...
template<typename T>
void Print(T val, char hex_mode) {
if (hex_mode == 'h')
Print(to_hstring<T>(val);
else
Print(to_string<T>(val);
}
如果T = double
或者T = float
它需要解决这个重载版本:
template<typename T>
void Print(T val, unint8_t decimal_places) { Print(to_string<T>(val, decimal_places); }
我已经能够编译它了,但是现在我遇到了带有未定义引用的链接器问题……这是我当前状态下的类。
#pragma once
#include "cstr.h"
class Foo {
public:
Foo(/*params*/) :
/*default initialize internal members*/
{}
template<typename T>
void Print(const char* str);
template<typename T>
void Print(char chr);
template<typename T>
void Print(T val);
template<typename T>
void Print(T val, const char hex_mode);
template<typename T>
void Print(T val, uint8_t decimal_places);
//void Print(uint64_t val, char hex_mode = '0');
//void Print(uint32_t val, char hex_mode = '0');
//void Print(uint16_t val, char hex_mode = '0');
//void Print(uint8_t val, char hex_mode = '0');
//void Print(int64_t val);
//void Print(int32_t val);
//void Print(int16_t val);
//void Print(int8_t val);
//void Print(double val, uint8_t decimal_places = default_decimal_places);
//void Print(float val, uint8_t decimal_places = default_decimal_places);
private:
void PutChar(char c);
// Private Members
};
template<typename T>
inline void BasicRenderer::Print(const char* str) {
char* chr = (char*)str;
while(*chr != 0) {
if (*chr == '\n') {
PutChar('\n');
chr++;
} else {
PutChar(*chr);
cursor_position_.x += 8;
if (cursor_position_.x + 8 > framebuffer_->Width) {
cursor_position_.x = 0;
cursor_position_.y += 16;
}
chr++;
}
}
}
template<typename T>
inline void BasicRenderer::Print(char c) { PutChar(c); }
template<typename T>
inline void BasicRenderer::Print(T val, const char hex_mode) {
if (hex_mode == 'h')
Print(to_hstring<T>(val));
else
Print(to_string<T>(val));
}
template<typename T>
inline void BasicRenderer::Print(T val, const uint8_t decimal_places) {
Print(to_string<T>(val, decimal_places));
}
这是 GCC 的链接器错误...Foo
上面只是实际类名的伪名称,可以在链接器错误输出中看到。
skilz420@skilz-PC:~/skilzOS/kernel$ make kernel
!==== LINKING
ld -T kernel.ld -static -Bsymbolic -nostdlib -o bin/kernel.elf lib/kernel.o lib/cstr.o lib/BasicRenderer.o
ld: lib/kernel.o: in function `_start':
kernel.cpp:(.text+0x3a): undefined reference to `void BasicRenderer::Print<char const*>(char const*)'
ld: kernel.cpp:(.text+0x4b): undefined reference to `void BasicRenderer::Print<unsigned long>(unsigned long)'
ld: kernel.cpp:(.text+0x5c): undefined reference to `void BasicRenderer::Print<char>(char)'
ld: kernel.cpp:(.text+0x6f): undefined reference to `void BasicRenderer::Print<long>(long)'
ld: kernel.cpp:(.text+0x80): undefined reference to `void BasicRenderer::Print<char>(char)'
ld: kernel.cpp:(.text+0x98): undefined reference to `void BasicRenderer::Print<double>(double)'
ld: kernel.cpp:(.text+0xa9): undefined reference to `void BasicRenderer::Print<char>(char)'
ld: kernel.cpp:(.text+0xd0): undefined reference to `void BasicRenderer::Print<char>(char)'
ld: kernel.cpp:(.text+0xe4): undefined reference to `void BasicRenderer::Print<float>(float)'
ld: lib/kernel.o: in function `void BasicRenderer::Print<unsigned int>(unsigned int, char)':
kernel.cpp:(.text._ZN13BasicRenderer5PrintIjEEvT_c[_ZN13BasicRenderer5PrintIjEEvT_c]+0x36): undefined reference to `void BasicRenderer::Print<char const*>(char const*)'
ld: kernel.cpp:(.text._ZN13BasicRenderer5PrintIjEEvT_c[_ZN13BasicRenderer5PrintIjEEvT_c]+0x54): undefined reference to `void BasicRenderer::Print<char const*>(char const*)'
make: *** [Makefile:33: link] Error 1
skilz420@skilz-PC:~/skilzOS/kernel$
我需要做些什么来解决这个问题以解决这些链接器错误,并让类的Print()
函数适当地处理每种类型?我不确定我在这里做错了什么......
-笔记-
- 除了定义的整数类型外,我没有使用任何
STL
其他类型。stdint.h
请不要建议使用STL
,因为这是Kernel
项目的一部分!- 我没有使用
std::cin
,std::cout
,也没有使用任何C
printf()
功能或其任何变体......
- 我没有使用
- 在其他来源中调用这些函数时,我也不想这样做:
obj.Print<type>(...);
我希望通过这样调用它们来自动解决它们:obj.Print(...);
- 如果您出于某种原因需要查看
cstr.h
实现,或其他未显示的功能,请不要犹豫,问... - 如果您需要有关此
kernel
项目的更多信息,这里有一些资源:
解决方案
您必须实现自己的type_traits
模板。这不是很难。
下面是使用标准 type_traits 的示例。
- 编辑:添加自定义 type_traits 示例。您必须实现其他诸如
is_floating_point
、is_unsigned
和其他is_integral
专业化。
namespace custom {
template<typename T, T v>
struct integral_constant {
using type = T;
static constexpr T value = v;
};
using false_type = integral_constant<bool, false>;
using true_type = integral_constant<bool, true>;
template <bool, typename T = void> struct enable_if {};
template <typename T> struct enable_if<true, T> { using type = T;};
template<bool v, typename T> using enable_if_t = typename enable_if<v, T>::type;
template<typename T> struct remove_const { using type = T;};
template<typename T> struct remove_const<const T> { using type = T; };
template<typename T> using remove_const_t = typename remove_const<T>::type;
template<typename T> struct remove_volatile { using type = T; };
template<typename T> struct remove_volatile<volatile T> { using type = T; };
template<typename T> using remove_volatile_t = typename remove_volatile<T>::type;
template<typename T> struct remove_cv { using type = remove_volatile_t<remove_const_t<T>>; };
template<typename T> using remove_cv_t = typename remove_cv<T>::type;
template<typename T> struct is_integral_impl : public false_type {};
template<> struct is_integral_impl<bool> : public true_type {};
template<> struct is_integral_impl<char> : public true_type {};
template<> struct is_integral_impl<int> : public true_type {};
// others...
template<typename T> struct is_integral : public is_integral_impl<remove_cv_t<T>> {};
}
class Foo {
public:
Foo(/*params*/) :
/*default initialize internal members*/
{}
// No templates. Just overload
void Print(const char* str);
// floating-point blocker
template<typename T, custom::enable_if_t<custom::is_integral<T>::value, int> = 0>
void Print(T val);
// specialization
template<>
void Print<char>(char chr);
// SFINAE because char and uint8_t is can be same
template<typename T, custom::enable_if_t<custom::is_unsigned<T>::value, int> = 0>
void Print(T val, const char hex_mode);
template<typename T, custom::enable_if_t<custom::is_floating_point<T>::value, int> = 0>
void Print(T val, uint8_t decimal_places);
};
推荐阅读
- c++ - 在 C++ 中向 Tensorflow lite 模型提供一批输入
- dart - dart http 在发送请求之前给我 400,响应的正文是空白字符串
- visual-studio - ComboBox.Item.add(USB1)切换语言-----全球化-VS 2019 WPF
- excel - 在一个单元格中匹配多个部分文本并在 excel 中返回最大日期
- editor - 如何在 pine 编辑器中的指定时间范围和图表会话中为我的条形高点创建水平线
- visual-studio - c#:如何强制类表示层调用 Dal 层?
- docker - 考虑 URL 重定向:如何在远程服务器上的同一 docker 网络中使用在不同容器中运行的 Web 应用程序的 GUI?
- list - Discord.py:如何使用 discord.Profile.connected_accounts
- javascript - 在javascript中的两个选择框之间交换所有值
- python - 在 Python 中运行的套接字服务器和网络服务器