Macro’s vs Variadic Templates – 2

Former article was visited by C++ fans from around the world. Therefore i decided to publish my article in english. Thanks for visiting web site.

More Generic Design
You know templates provide more generic design than function overloading. And now with C++11, you can even design more generic templates than templates, so variadic templates.

I want to write a function that gives me size of all containers in STL(even if they almost all have a size function ­čÖé ). You know STL has two main container types:
– Sequence Containers
– Associative Containers

Sequence containers take 2 arguments ValueType and mainly Allocator:

template<class T,
         class Allocator = std::allocator<T>
         > class SeqContainer;

I will try to implement my function-template for sequence containers.

template < template <typename, typename> class ContainerType, typename ValueType, typename Alloc>
size_t sizeOfContainers(ContainerType<ValueType, Alloc> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}

As you see first template parameter “template class ContainerType” is a template class as template parameter. Second and third one are template parameters of first parameter.

But that function template will not work for std::array type. We will look that problem later. Mainly this function satisfies our needs.

#include <vector>
#include <iostream>
#include <memory>
#include <map>
#include <forward_list>
#include <unordered_set>
#include <array>

using namespace std;

template < template <typename, typename> class ContainerType, typename ValueType, typename Alloc>
size_t sizeOfContainers(ContainerType<ValueType, Alloc> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}



int main()
{
    vector<int> v({1, 4, 5, 3, 5, 6});
    forward_list<int> fl ({ 34, 99, 10, 71});
    
    cout << sizeOfContainers(v) << endl;
    cout << sizeOfContainers(fl) << endl;

   
  return 0;
}

Associative containers take 3 or 4 arguments for example:

std::set

template<
    class Key,
    class Compare = std::less<Key>,
    class Allocator = std::allocator<Key>
> class set;

std::map

template<
    class Key,
    class T,
    class Compare = std::less<Key>,
    class Allocator = std::allocator<std::pair<const Key, T> >
> class map;

std::unordered_set

 
template<
    class Key,
    class Hash = std::hash<Key>,
    class KeyEqual = std::equal_to<Key>,
    class Allocator = std::allocator<Key>
> class unordered_set;

Therefore we need two more overload for our function to handle this containers. Let’s try to write them :


template < template <typename, typename, typename> class ContainerType, typename FirstType, typename SecondType, typename AllocType>
size_t sizeOfContainers(ContainerType<FirstType, SecondType,  AllocType> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}

template < template <typename, typename, typename, typename> class ContainerType, typename FirstType, typename SecondType, typename ThirdType, typename AllocType>
size_t sizeOfContainers(ContainerType<FirstType, SecondType, ThirdType, AllocType> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}

As you see for that functions can handle this issue for different type.

But now we have three overloads, can we reduce it to one function with variadic functions ? Lets try it:

template <template <typename, typename...> class ContainerType, typename FirstType, typename... Types> 
size_t sizeOfContainers(ContainerType<FirstType, Types...> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}

Now we can handle all 2,3,4 parameter containers. Except one std::array. std::array takes two parameter:

template< 
    class T, 
    std::size_t N 
> struct array;

Remember, we discussed variadic template function creation at first article. In that example our variadic function will not create function that we need for std::array. Actually second type of std::array is not a type, it is a constant. And it is not allowed in C++, also compile time error. Function overloading take responsibility for us :

template <template <typename, size_t> class ContainerType, typename FirstType, size_t S> 
size_t sizeOfContainers(ContainerType<FirstType, S> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}

That overload will handle our issue. Sometimes we need partial specialization overloads to resolve problems.

At the end of the day score is on the board:

Variadic templates 3 – 1 Function overloading

Full Source Code:

#include <vector>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <map>
#include <forward_list>
#include <unordered_set>
#include <array>
#include <set>
#include <deque>

using namespace std;


template <template <typename, typename...> class ContainerType, typename FirstType, typename... Types> 
size_t sizeOfContainers(ContainerType<FirstType, Types...> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}


template <template <typename, size_t> class ContainerType, typename FirstType, size_t S> 
size_t sizeOfContainers(ContainerType<FirstType, S> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}

template < template <typename, typename> class ContainerType, typename ValueType, typename Alloc>
size_t sizeOfContainers(ContainerType<ValueType, Alloc> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}

template < template <typename, typename, typename> class ContainerType, typename FirstType, typename SecondType, typename Alloc>
size_t sizeOfContainers(ContainerType<FirstType, SecondType,  Alloc> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}

template < template <typename, typename, typename, typename> class ContainerType, typename FirstType, typename SecondType, typename ThirdType, typename Alloc>
size_t sizeOfContainers(ContainerType<FirstType, SecondType, ThirdType, Alloc> &c)
{
    size_t s = 0;
    for (auto element : c) {
        s++;
    }
    return s;
}



int main()
{
    vector<int> v({1,2,3,4,5,6});
    map<int, int> m({ {2,3},  {4, 5}});
    set<int> s({8, 7});
    deque<int> d = {10,20,30};
    forward_list<int> fl ({ 34, 77, 16, 2 });
    array<int,10> a = { 2, 16, 77, 34, 50};
    unordered_set<string> us = {"C++", "UG","ISTANBUL","69736c616d"};
    

    cout << sizeOfContainers(v) << endl;
    cout << sizeOfContainers(m) << endl;
    cout << sizeOfContainers(fl) << endl;
    cout << sizeOfContainers(us) << endl;
    cout << sizeOfContainers(s) << endl;
    cout << sizeOfContainers(a) << endl;
    cout << sizeOfContainers(d) << endl;
    
  return 0;
}
Advertisements

Debugging with GDB – C++ UG Meetup

28 Kas─▒m Cuma g├╝n├╝ saat 15:00’de “Debugging with GDB” adl─▒ seminerimize herkes davetlidir.

Debugging with GDB

Friday, Nov 28, 2014, 3:00 PM

Teknopark Istanbul Konferans Salonu
Teknopark Istanbul Pendik Istanbul, TR

3 C++’ers Attending

Check out this Meetup →

Macro’s vs Variadic Templates – 1

Variadic fonksiyonlar istenilen say─▒da de─či┼čken almalar─▒ sayesinde bir ├žok i┼či kolayla┼čt─▒rm─▒┼člard─▒r. C++11 den ├Ânce C de bu ihtiya├ž “…” elipsis operat├Âr├╝ ve va_* makrolar─▒yla ├ž├Âz├╝l├╝yordu. Fakat bu ├ž├Âz├╝m ├žal─▒┼čma zaman─▒nda i┼čledi─činden yanl─▒┼č kullan─▒m veya beklenmedik durumlarda ├žal─▒┼čma zaman─▒ hatalar─▒ veya seg fault dahi al─▒nabiliyordu.

├ľrne─čin;

int a ;

printf("%s", a);

Bu kod ├Ârne─či derleme zaman─▒nda (compile time) hata vermez. Fakat ├žal─▒┼čma zaman─▒nda seg fault al─▒rs─▒n─▒z. C++11 de bu problem variadic templatelerle ├ž├Âz├╝l├╝yor. ├ťstelik derleme zaman─▒nda ├ž├Âz├╝ld├╝─č├╝ i├žin yanl─▒┼č kullan─▒m durumunda hemen m├╝dahale edebiliyorsunuz.

Variadic templates

Basit bir ├Ânekle devam edelim;

#include <iostream>

using namespace std;

template<typename T>
void println(T v) //A
{
    cout << v << endl;
}

template<typename T, typename... Types>
void println(T first, Types... values) //B
{
    println(first);
    println(values...);
}

int main()
{
   println("islam", "cppistanbul", 1299); //1
   println(6, 1, 0, "c++UG", 5, 7, 1); //2
  
  return 0;
}

“typename… Types” ┼čablon parametre paketi(template parameter pack), “Types… values” ise fonksiyon parametre paketi (function parameter pack) ┼čeklinde adland─▒r─▒l─▒yor. template parameter packteki Types asl─▒nda bir t├╝r listesi. ├ľrne─čin “1” durumunda bu liste “const char *, const char *, integer” ┼čeklindedir.Function parameter pack ise bir de─čer listesidir. Yine “1” durumunda bu liste “islam, cppistanbul, 1299” ┼čeklindedir. Elipsis (…) operat├Âr├╝yle de bu de─čerler ve t├╝rler a├ž─▒l─▒r(expand). Burda dikkat ederseniz iki tane println fonksiyonu var, basit├že bir oveload asl─▒nda. Bu overload ile asl─▒nda k─▒smi bir rek├╝rsiv ├ža─čr─▒ yap─▒yoruz. Daha iyi anla┼č─▒lmas─▒ i├žin ad─▒m ad─▒m ilerleyelim.

1.iterasyon:

“1” durumunda println fonksiyonu 3 t├╝r ie ├ža─č─▒r─▒lm─▒┼č. Template argument deduction yaparak B deki overload ├ža─čr─▒lacak ve:

void println(T, Types ...) [with T = const char*; Types = {const char*, int}]

parametre bu ┼čekilde belirlenecek. Derleyici bir tane yukar─▒daki imzaya sahip bir fonksiyon olu┼čturacak. Ve dikkat edin A durumundaki tek parametreli template de ┼ču ┼čekilde derleyici taraf─▒ndan yaz─▒lacak:

void println(T) [with T = const char*]

2. iterasyon:

B template fonksiyonu:

void println(T, Types ...) [with T = const char*; Types = {int}] 

┼čeklinde, A template fonksiyonu ise yine ayn─▒ imzaya sahip olaca─č─▒ndan tekrar yaz─▒lmayacak.

3. iterasyon

Bu durumda ise sadece tek t├╝r kald─▒g─▒ndan direkt olarak A template fonksiyonu tercih edilecek(argument deduction). Ve a┼ča─č─▒daki imzaya sahip bir fonksiyon derleyici taraf─▒ndan yaz─▒lacak:

void println(T) [with T = int]

Bu sayede println fonksiyonuna verdi─čimiz de─čerlere uygun overload edilmi┼č println fonksiyonu ├ža─č─▒ralarak ekrana bu de─čerler yaz─▒lm─▒┼č olacak.

To be continued :))