0

Consider the following overloaded function that can print a 1d vector and a vector of vector of several types like strings, ints, doubles etc.

template<typename T>
void p(const vector<vector<T>>& vec) {
    int count = 0;
    for (vector<T> innerVec: vec) {
        cout << count++ << ": ";
        for (T e :innerVec) {
            cout << e << ' ';
        }
        cout << '\n';
    }
    cout << '\n';
}


template<typename T>
void p(const vector<T>& vec) {
    for (T e: vec) {
        cout << e << ' ';
    }
    cout << '\n';
}

Is there anyway I can merge these two functions into 1? I tried using SFINAE and tag dispatching but all the solutions I could come up with need a macro or multiple functions and I don't want this.

I know the question might seem odd since my solution works, but I prefer having just one function in my code. This is because I want to implement a function that can detect if I am passing in a map, vector, vector of vectors, unordered_set, multimap, etc and just print that STL data structure and having one overloaded function for each specialization is a bit annoying as it gets large quick.

Hisham Hijjawi
  • 1,803
  • 2
  • 17
  • 27
  • 1
    Not a full solution, but you can replace the code duplication by, in the 2D version, just doing `cout << innerVec;` Also, I'd really recommend passing by `const &` – ChrisMM Feb 19 '20 at 18:10
  • Unrelated: `vector> vec` is not a 2D `vector`. It is a `vector` that contains more `vector`s. There can be large performance implications because of this. The data is not all in one contiguous memory block, so it's not particularly cache friendly. – user4581301 Feb 19 '20 at 18:15
  • Because the behaviour of the two functions is different you'll have trouble merging the two functions You could do something like this: `template std::ostream & operator<<(std::ostream & out, const std::vector& vec) { for (T e: vec) { out << e << delimiter; } return out; }` but the delimiter changes. You need some extra voodoo to detect when you're at a leaf, as in there are no more containers, so you can swap the delimiter. I admit to sucking at voodoo. – user4581301 Feb 19 '20 at 18:55
  • If you drop the `0: ` etc from the desired output, then you only need `template> std::ostream & operator<<(std::ostream & out, const Container& cont)` and `template std::ostream & operator<<(std::ostream & out, const std::pair& pair)` to cover everything. – Caleth Feb 19 '20 at 18:59

2 Answers2

1

I answered a similar question today. Check it out there: https://stackoverflow.com/a/60298735/8192043

Pasting the solution here:

This should work for your case. Note that I'm using a trait as implemented here in this amazing solution by @Jarod42 https://stackoverflow.com/a/29634934/8192043.

template<template<typename ...> typename C, typename D, typename ... Others>
void foo(const C<D, Others...> &object)
{
    if constexpr(is_iterable<D>::value)
    {
       for(const auto& v : object)
       {
           for (const auto& w : v)
           {...}
       }
    }
    else
    {
       for (const auto& w : object)
       {...}
    }
}

Live Code

theWiseBro
  • 1,439
  • 12
  • 11
  • 1
    I'm trying to avoid adding the trait if possible. I don't want to add another function/class/struct just to be able to do something this simple. But clever idea so upvoted. – Hisham Hijjawi Feb 19 '20 at 20:51
0

Yes, but you need an extra parameter to distinguish the inner from the outer case

#include <vector>
#include <iostream>

struct counting_prefix {
    void call() { std::cout << count++ << ": "; }
    int count = 0;
};

struct no_prefix {
    void call() { }
};

template<typename T, typename Prefix = no_prefix>
void p(const T& e, Prefix prefix = {}) {
    prefix.call();
    std::cout << e << ' ';
}

template<typename T, typename Prefix = no_prefix>
void p(const std::vector<T>& vec, Prefix prefix = {}) {
    for (const T& e: vec) {
        prefix.call();
        p(e);
    }
    std::cout << '\n';
}

int main() {
    std::vector<std::vector<double>> stuff = { { 1., 2. }, { 3., 4. } };
    p(stuff, counting_prefix{});
}

See it live

Caleth
  • 52,200
  • 2
  • 44
  • 75