I'm learning about template in C++, and my set of C++ terminology is somewhat limited, so I couldn't google this problem.
I'm trying to implement a custom dict
type based on std::unordered_map
. My goal is to be able to instantiate the class dict
in ways like the following:
dict<std::string, long> d; // OR
dict<std::string, std::set<std::string>> d; // OR
dict<std::string, std::map<char, float>> d; // OR
dict<std::string, std::vector<std::string>> d; // OR
dict<std::string, std::vector<double>> d;
So here's the code, I'm using:
utils.h
#include <fstream>
#include <unordered_map>
#include <set>
#include <vector>
#include <algorithm>
#include <type_traits>
// for bravity
using namespace std;
// to check for stl set
// slightly modified version of: https://stackoverflow.com/a/31105859
namespace is_container {
template <typename T> struct stl_vector : false_type{};
template <typename T> struct stl_vector<std::vector<T>> : true_type{};
}
namespace StringOps {
// generaic function to split based on many delimiters:
// source: https://stackoverflow.com/a/9676623
vector<string> split(const string& str, const string& delimiters = " ,") {
vector<string> v;
unsigned start = 0;
auto pos = str.find_first_of(delimiters, start);
while(pos != string::npos) {
if(pos != start) // ignore empty tokens
v.emplace_back(str, start, pos - start);
start = pos + 1;
pos = str.find_first_of(delimiters, start);
}
if(start < str.length()) // ignore trailing delimiter
v.emplace_back(str, start, str.length() - start); // add what's left of the string
return v;
}
}
template<class Key, template <class...> class Value, typename T, class = void>
class dict {
public:
Value<T> t;
};
template<class Key, template <class...> class Value, typename T> // detect container types with ::iterator
class dict<Key, Value, T, void_t<typename Value<T>::iterator>> : true_type {
private:
unordered_map<Key, Value<T>> content;
bool is_vector = false;
string line;
unordered_map<Key, Value<T>> load(ifstream& file) {
while (getline(file, line)) {
if (!line.empty()) {
// remove trailling \n if exists
if (line[line.length()-1] == '\n')
line.erase(line.length() - 1);
vector<string> tokens = StringOps::split(line);
Value<T> result;
// for (unsigned i = 1; i < tokens.size(); i++)
// result.emplace_hint(it, static_cast<T>(tokens[i]));
if (is_vector) {
for (unsigned i = 1; i < tokens.size(); i++) {
result.emplace_back(static_cast<T>(tokens[i]));
}
}
if(false) {
auto it = result.cend();
for (unsigned i = 1; i < tokens.size(); i++) {
result.emplace_hint(it, static_cast<T>(tokens[i]));
}
}
content[static_cast<Key>(tokens[0])] = result;
}
}
return content;
}
public:
constexpr Value<T>& operator[](Key k) {
return content[k];
}
dict(const string& path) {
// detect vector type
if(is_container::stl_vector<decay_t<Value<T>>>::value)
is_vector = true;
ifstream file(path);
content = load(file);
}
constexpr unsigned size() {
return content.size();
}
};
template<class Key, template <class...T> class Value, typename T> // detect arithmatic types
class dict<Key, Value, T, typename enable_if<is_arithmetic<Value<T>>::value>::type> {
public:
dict() {
// we'll come to you later..
}
};
main.cpp
#include <iostream>
#include "utils.h"
int main() {
dict<string, vector, string> d("/home/path/to/some/file");
cout << d.size();
}
results:
error: no member named 'emplace_hint' in 'std::vector<std::__cxx11::basic_string<char>, std::allocator<std::__cxx11::basic_string<char> > >'
result.emplace_hint(it, static_cast<T>(tokens[i]));
questions: 1 - why on earth if (false)
condition is reached in the first place? 2 - how could this be tweaked to achieve the desired instantiation style?
Aucun commentaire:
Enregistrer un commentaire