8

I've written an asynchronous job queue class which has been working nicely for ages. It uses a std::vector as the underlying collection to keep jobs in and then processes them later as you might expect. When I add a job it does a push_back on this vector.

Recently I decided that I wanted to templatize the underlying collection type that it uses and the with way I've written it, this should be very simple. It's now declared thus:

template<typename J, typename CollectionT = std::vector<J>>
class async_jobqueue
{
public:

There's just one snag, for vectorish type containers I want to push things onto the end of the collection and call push_back, for settish type containers I'll want to call insert. How can I make a compile decision about which to call? Or is there a handy adapter I can use?

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
Benj
  • 31,668
  • 17
  • 78
  • 127
  • 1
    Check this out: http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence. It blew my mind when I read it. – SirPentor Mar 22 '13 at 15:50
  • @SirPentor That's cool although it looks like that would give me a way of figuring it out at run time. – Benj Mar 22 '13 at 15:52
  • Either use `SFIANE`(http://en.wikipedia.org/wiki/SFINAE) or specialize or template. – phoeagon Mar 22 '13 at 15:52
  • @Benj You can use it in combination with `std::enable_if` or `static_asset` to do it at compile time. – Joseph Mansfield Mar 22 '13 at 15:53
  • @Benj How are you deciding what is a vectorish or settish type of container? Do you mean a sequence container vs. associative container? – Joseph Mansfield Mar 22 '13 at 15:54
  • Also this: http://stackoverflow.com/questions/14541519/is-it-possible-to-write-c-template-macros-to-check-whether-two-functions-have – SirPentor Mar 22 '13 at 15:54
  • @sftrabbit I'm not doing anything clever to decide this but I want the template to work with `std::vector`, `std::priority_queue` and `std::set`, my code is sufficiently generic I don't need to do much clever detection other than to work out whether to call push_back or insert... – Benj Mar 22 '13 at 15:57
  • `enable_if` looks promising – Benj Mar 22 '13 at 15:58

3 Answers3

7

I would rather use an overloaded helper function. The one below relies on the fact that no Standard container exposes both a single-argument insert() function and a push_back() function:

#include <utility>

template<typename C, typename T>
auto insert_in_container(C& c, T&& t) ->
    decltype(c.push_back(std::forward<T>(t)), void())
{
    c.push_back(std::forward<T>(t));
}

template<typename C, typename T>
auto insert_in_container(C& c, T&& t) ->
    decltype(c.insert(std::forward<T>(t)), void())
{
    c.insert(std::forward<T>(t));
}

This is how you would use them:

#include <set>
#include <vector>
#include <iostream>

int main()
{
    std::set<int> s;
    std::vector<int> v;

    insert_in_container(s, 5);
    insert_in_container(v, 5);

    std::cout << s.size() << " " << v.size();
}

And here is a live example.

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • Are you using `is_same` like that to induce SFINAE? – David G Mar 22 '13 at 16:05
  • @David: Yes, that's just a trick. There could be other ways of course – Andy Prowl Mar 22 '13 at 16:06
  • Maybe you can use: `-> decltype(c.insert(std::forward(t)), void())` to cut down on code bloat. – David G Mar 22 '13 at 16:08
  • @David: Right, this is much cleaner, let me edit the answer. Thank you! – Andy Prowl Mar 22 '13 at 16:09
  • Sadly although g++ compiles this, VC2010 says: `w32threads.h(45): error C2995: 'void insert_in_container(C &,T &&)' : function template has already been defined` – Benj Mar 22 '13 at 16:17
  • @Benj: Have you tried also the updated version of this answer? – Andy Prowl Mar 22 '13 at 16:19
  • @AndyProwl - Yes I tried both, VC2010 appears to object to multiple function templates with the same name. I guess I could work around it by creating a template class with two overloaded functions which do the same thing.... – Benj Mar 22 '13 at 16:21
  • @Benj: In that case I guess you will have to figure out another criterion for decide whether to call `insert()` or `push_back()` than just checking which one of the two expressions compile (which is what I am doing). That mostly depends on the range of types you want to support – Andy Prowl Mar 22 '13 at 16:32
  • @benj VC doesn't support much of c++11 i've heard. – David G Mar 22 '13 at 20:42
  • @David All the c++11 features referenced in this post are available in VC2010...in theory. – Benj Mar 23 '13 at 00:05
4

How about the insert(iterator, value_type) overload and calling it with end()? It's available in both and should do what you want! Works on std::list as well!

There's really no need for type-dispatching here.

ltjax
  • 15,837
  • 3
  • 39
  • 62
2

Since concepts-lite should hopefully be popping up in time for C++14, I might as well show you how this will be done then:

template<typename J, typename CollectionT = std::vector<J>>
class async_jobqueue
{
  public:

    requires Associative_container<CollectionT>()
    void adding_function(const J& item) {
      // Uses insert
    }

    requires Sequence_container<CollectionT>()
    void adding_function(const J& item) {
      // Uses push_back
    }
};

Of course, this is not possible yet (and may never be). However, the reception to concepts-lite is pretty positive.

Joseph Mansfield
  • 108,238
  • 20
  • 242
  • 324