1
\$\begingroup\$

Previous question:

Counting words in files - follow-up 2

Continuation of the meta programming template version of counting words. Inspired by example of pirntf given by Andre Alexandrescu at GoingNative 2012.

How can I improve this code?

//
//program name: WordCounter
//
//usage: C:\>WordCounter.exe test.txt test1.txt 
//
#include <iostream>
#include <iomanip>
#include <fstream>
#include <unordered_map>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
#include <type_traits>
#include <stdexcept>
#include <sstream>

template<bool...>
struct BoolSequence
{
}; 

template<std::make_unsigned<char>::type ...>
struct CharSequence
{
};  

template<typename T, std::make_unsigned<char>::type  C>
struct Contains;

template<std::make_unsigned<char>::type  First,
    std::make_unsigned<char>::type ... Cs, 
    std::make_unsigned<char>::type  Match>  
struct Contains<CharSequence<First, Cs...>, Match>
    : Contains<CharSequence<Cs...>, Match>
{
};

template<std::make_unsigned<char>::type  First, std::make_unsigned<char>::type ... Cs>          
struct Contains<CharSequence<First, Cs...>, First>
    : std::true_type
{
};
template<std::make_unsigned<char>::type  Match>
struct Contains<CharSequence<>, Match>
    : std::false_type
{
};

template<std::make_unsigned<int>::type I, typename T, typename U>
struct MakeSequence;

template<std::make_unsigned<int>::type I, bool... Bs, typename U>
struct MakeSequence<I, BoolSequence<Bs...>, U>
    :MakeSequence<I - 1, BoolSequence<Contains<U, I - 1>::value, Bs...>, U>
{
};

template<bool... Bs, typename U>
struct MakeSequence<0, BoolSequence<Bs...>, U>
{
    using Type = BoolSequence<Bs...>;
};

template<typename T>
struct BoolASCIITable;

template<bool... Bs>
struct BoolASCIITable<BoolSequence<Bs...>>
{
    bool operator()(const int c) const
    {
        static const bool table[] = { Bs... };
        return table[c];
    }
    using argument_type = int;
};

using Delims = CharSequence<'.', ','>; 
using Table = BoolASCIITable<typename MakeSequence<256, BoolSequence<>, Delims>::Type>;
using Words = std::unordered_map<std::string, std::size_t>;

template<typename T>
std::pair<T, T> getToken(T begin, T end)
{
    begin = find_if(begin, end, std::not1(Table{})); 
    auto second = find_if(begin, end, Table{});     
    return std::make_pair(begin, second);
}

struct bad_format : public std::runtime_error
{
    bad_format(const char* msg) : runtime_error(msg) {}
};

template <typename T>
typename std::enable_if<std::is_integral<T>::value, long>::type
normalizeArg(T arg)
{
    return arg;
}

template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, double>::type
normalizeArg(T arg)
{
    return arg;
}

template <typename T>
typename std::enable_if<std::is_pointer<T>::value, T>::type
normalizeArg(T arg)
{
    return arg;
}

inline
const char* normalizeArg(const std::string& arg)
{
    return arg.c_str();
}

inline
const char* normalizeArg(bool arg)
{
    return arg ? "true" : "false";
}

inline
void check(const char* f)
{
    for (; *f; ++f) {
        if (*f != '%' || *++f == '%')
            continue;
        throw bad_format("missing arguments to pr()");
    }
}

inline
void enforce(bool cond, const char* msg)
{
    if (!cond)
        throw bad_format(msg);
}

inline
const char* skip_modifiers(const char* f)
{
    while (*f && (*f == '-' ||
        *f == '+' ||
        *f == ' ' ||
        *f == '#' ||
        *f == '*' ||
        *f == '.' ||
        *f == 'h' ||
        *f == 'l' ||
        *f == 'L' ||
        *f == 't' ||
        *f == 'z' ||
        isdigit(*f)))
        ++f;
    return f;
}

template <typename T>
void check_type(const char* f)
{
    switch (*f)
    {
    default: throw bad_format("unknown format code");
    case 'f': case 'g': case 'e': case 'E': case 'G': case 'a': case 'A':
        enforce(std::is_floating_point<T>::value, "non-floating point value to %(f) or %(g)");
        break;
    case 's':
        enforce(std::is_same<T, bool>::value ||
            std::is_same<T, std::string>::value ||
            std::is_same<T, const char*>::value, "non-string value to %(s)");
        break;
    case 'd': case 'i':
        enforce(std::is_signed<T>::value, "non-signed value to %(d) or %(i)");
        break;
    case 'u':
        enforce(std::is_unsigned<T>::value, "non-unsigned value to %(u)");
        break;
    case 'p':
        enforce(std::is_pointer<T>::value, "non-pointer value to %(p)");
        break;
    case 'x': case 'X': case 'o':
        enforce(std::is_integral<T>::value, "non-integral value to %(x) or %(X)");
        break;
    case 'c':
        enforce(std::is_integral<T>::value, "non-char value to %(c)");
        break;
    case 'n':
        enforce(false, "%(n) is not allowed");
        break;
    }
}

template <class T, typename... Ts>
void check(const char* f, const T& t, const Ts&... ts)
{
    for (; *f; ++f)
    {
        if (*f != '%' || *++f == '%') continue;
        f = skip_modifiers(f);
        check_type<T>(f);
        return check(++f, ts...);
    }

    throw bad_format("missing arguments to pr()");
}

void to_str_rec(std::stringstream& ss)
{
}

template <class T, typename... Ts>
void to_str_rec(std::stringstream& ss, const T& t, const Ts&... ts)
{
    ss << " " << t;
    to_str_rec(ss, ts...);
}

template <typename... Ts>
int fpr(FILE* fil, const char* f, const Ts&... ts)
{
    check(f, ts...);
    return fprintf(fil, f, normalizeArg(ts)...);
}

template <typename... Ts>
int fpn(FILE* fil, const char* f, const Ts&... ts)
{
    const int r = fpr(fil, f, ts...);
    fputs("\n", fil);
    return r + 1;
}

template<typename... Ts>
int pr(const char* f, const Ts&... ts)
{
    return fpr(stdout, f, ts...);
}

template<typename... Ts>
int pn(const char* f, const Ts&... ts)
{
    return fpn(stdout, f, ts...);
}

template <class T, typename... Ts>
std::string to_str(const T& t, const Ts&... ts)
{
    std::stringstream ss;
    ss << t;
    to_str_rec(ss, ts...);
    return std::move(ss.str());
}

bool loadFromFile(const std::string& fileName, Words& words)
{
    std::ifstream file(fileName);

    if (!file)
    {
        return false;
    }

    for (std::string word; file >> word;)
    {
        auto s = getToken(word.begin(), word.end());
        ++words[std::string(s.first, s.second)];
    }

    return true;
}

template<typename T>
void showResult(const T& begin, const T& end)
{
    std::vector<T> words;

    words.reserve(std::distance(begin, end));

    for (auto i = begin; i != end; ++i)
    {
        words.push_back(i);
    }

    std::sort(words.begin(), words.end(),
        [&](const T& it1, const T& it2)
    {
        return it1->second > it2->second;
    });

    bool first = true;

    for (const auto& i : words)
    {
        if (first)
        {
            pn("\nThe most repeated element is: %s", i->first);
            pn("\n Word\tRepeated Times");
            pn("----------------------");
            first = false;
        }

        pn(" %-10s %6u", i->first, i->second);
    }
}

int main(int argc, char *argv[])
{
    Words words;

    for (int i = 1; i < argc; ++i)
    {
        if (!loadFromFile(argv[i], words))
        {
            pn("Can't open the file  %s", argv[i]);
        }
    }

    showResult(words.begin(), words.end());
}
\$\endgroup\$
  • \$\begingroup\$ Just as a general comment, posting five or six or more versions of the same code is probably not as productive as working on one version, getting it as good as you can make it and then posting. \$\endgroup\$ – Edward Dec 14 '14 at 16:58
  • \$\begingroup\$ @Edward .. thanks ... i will do it. but, really not intentionally to make extended question. i got suggestion to add feature for ignoring punctuations automatically. there are many ways to achieve this i post 2 versions. sorry for that. \$\endgroup\$ – MORTAL Dec 15 '14 at 4:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.