有关模板/元编程技术改进的一些问题。
下面代码的动机是使用 C 风格的 API 方便地从 SQL 数据库驱动程序中提取正确类型的数据。结果集的行被一一返回为 char**
。我用下面的一些数组文字模拟了这一点。
我把这个包起来char**
类中的行数据提供类型提取。还有一个模板化的 single_column()
函数可以在调用者选择的容器中返回单一类型的列。
代码的工作原理如图所示。问题是:
- 阻止
row::get<ValueType>
的非专业版本与static_cast<void>
看起来像黑客。有没有更好的办法?我认为答案不是简单地写get_int()
,get_long()
等等,因为我们想要像single_column()
这样的通用代码只选择指定的 getter。 - 类型特征“方法存在调度”来检测
push_back()
的存在与if constexpr
工作正常。有没有更优雅的方式使用C++17?即没有 c++20 概念?
非常感谢
#include <cstddef>
#include <iostream>
#include <string>
#include <unordered_set>
#include <vector>
// is there a more direct way (without using c++20 concepts)
template <typename C, typename = void>
struct has_push_back : std::false_type {};
template <typename C>
struct has_push_back<
C, std::void_t<decltype(std::declval<C>().push_back(std::declval<typename C::value_type>()))>>
: std::true_type {};
class row {
public:
explicit row(const char** row) : row_(row) {}
template <typename ValueType>
ValueType get(unsigned idx) const {
// should always fail! user must call an existing specialisation!
// is there a "better" way?
return static_cast<void>(row_[idx]); // NOLINT ptr arith
}
template <>
[[nodiscard]] int get<int>(unsigned idx) const {
return std::stoi(row_[idx]); // NOLINT ptr arith
}
template <>
[[nodiscard]] long get<long>(unsigned idx) const {
return std::stol(row_[idx]); // NOLINT ptr arith
}
template <>
[[nodiscard]] std::string get<std::string>(unsigned idx) const {
return row_[idx]; // NOLINT ptr arith
}
template <>
[[nodiscard]] const char* get<const char*>(unsigned idx) const {
return row_[idx]; // NOLINT ptr arith
}
private:
const char** row_;
};
template <typename ContainerType>
ContainerType single_column(const std::vector<row>& rows, unsigned col = 0) {
ContainerType values;
using ValueType = typename ContainerType::value_type;
for (auto&& row: rows) {
if constexpr (has_push_back<ContainerType>::value) // is this "best" way?
values.push_back(row.get<ValueType>(col));
else
values.insert(row.get<ValueType>(col));
}
return values;
}
int main() {
// dealing with this as c-arrays because that is how it comes from db driver
// represents raw data coming back from db
// NOLINTNEXTLINE C-arrays
const char* data[][3] = {
{
"1-some text",
"122222222",
"1333333333333333",
},
{
"2-some text",
"222222222",
"2333333333333333",
},
{
"2-some text", // dupe value
"322222222",
"3333333333333333",
},
};
std::vector<row> rows; // simulationg rows being returned the db server
for (auto& r: data) rows.emplace_back(r);
// now retrieve column-wise as correct types into vectors
for (auto&& e: single_column<std::vector<std::string>>(rows, 0)) std::cout << e << ",";
std::cout << std::endl;
for (auto&& e: single_column<std::vector<int>>(rows, 1)) std::cout << e << ",";
std::cout << std::endl;
for (auto&& e: single_column<std::vector<long>>(rows, 2)) std::cout << e << ",";
std::cout << std::endl;
// now into unordered_sets
for (auto&& e: single_column<std::unordered_set<std::string>>(rows, 0)) std::cout << e << ",";
std::cout << std::endl;
for (auto&& e: single_column<std::unordered_set<int>>(rows, 1)) std::cout << e << ",";
std::cout << std::endl;
for (auto&& e: single_column<std::unordered_set<long>>(rows, 2)) std::cout << e << ",";
std::cout << std::endl;
// compile error - the static_cast<void> fails: works, but is there a "better" way
// double d = rows[0].get<double>(1);
}
最佳答案
blocking the non-specialised version of row::get with a static_cast seems like a hack. Is there a better way. I don't thing the answer is to simply write get_int(), get_long() etc, because we want generic code like single_column() to just select the specified getter.
您可以删除
它:
template <typename ValueType>
ValueType get(unsigned idx) const = delete;
the type-trait "method exists dispatching" to detect presence of
push_back()
withif constexpr
works fine. Is there a more elegant way using C++17? ie without c++20 concepts?
你的方法很好(创建和使用类型特征);
C++20 概念允许使用 requires
提供更好的语法。
关于c++ - 强制使通用模板失败,但允许特化,加上 "method exists dispatching",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69220893/