vendredi 2 août 2019

C++ if (false) condition evaluated?

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