Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Advanced C++

You're reading from   Advanced C++ Master the technique of confidently writing robust C++ code

Arrow left icon
Product type Paperback
Published in Oct 2019
Publisher
ISBN-13 9781838821135
Length 762 pages
Edition 1st Edition
Languages
Arrow right icon
Authors (5):
Arrow left icon
Olena Lizina Olena Lizina
Author Profile Icon Olena Lizina
Olena Lizina
Rakesh Mane Rakesh Mane
Author Profile Icon Rakesh Mane
Rakesh Mane
Gazihan Alankus Gazihan Alankus
Author Profile Icon Gazihan Alankus
Gazihan Alankus
Brian Price Brian Price
Author Profile Icon Brian Price
Brian Price
Vivek Nagarajan Vivek Nagarajan
Author Profile Icon Vivek Nagarajan
Vivek Nagarajan
+1 more Show less
Arrow right icon
View More author details
Toc

Table of Contents (11) Chapters Close

About the Book 1. Anatomy of Portable C++ Software 2A. No Ducks Allowed – Types and Deduction FREE CHAPTER 2B. No Ducks Allowed – Templates and Deduction 3. No Leaks Allowed - Exceptions and Resources 4. Separation of Concerns - Software Architecture, Functions, and Variadic Templates 5. The Philosophers' Dinner – Threads and Concurrency 6. Streams and I/O 7. Everybody Falls, It's How You Get Back Up – Testing and Debugging 8. Need for Speed – Performance and Optimization 1. Appendix

Chapter – 2B - No Ducks Allowed – Templates and Deduction

Activity 1: Developing a Generic "contains" Template Function

In this activity, we will implement several helper classes that will be used to detect the std::string class case and the std::set case and then use them to tailor the contains function to the particular container. Follow these steps to implement this activity:

  1. Load the prepared project from the Lesson2B/Activity01 folder. Build and configure the launcher and run the unit tests (which fail the one dummy test). We recommend that the name that's used for the tests runner is L2BA1tests.
  2. Open the containsTests.cpp file and replace the existing test with the following:

    TEST_F(containsTest, DetectNpos)

    {

        ASSERT_TRUE(has_npos_v<std::string>);

        ASSERT_FALSE(has_npos_v<std::set<int>>);

        ASSERT_FALSE(has_npos_v<std::vector<int>>);

    }

    This test requires us to write a set of helper templates to detect if the container class supports a static member variable called npos.

  3. Add the following code to the contains.hpp file:

    template <class T>

    auto test_npos(int) -> decltype((void)T::npos, std::true_type{});

    template <class T>

    auto test_npos(long) -> std::false_type;

    template <class T>

    struct has_npos : decltype(test_npos<T>(0)) {};

    template< class T >

    inline constexpr bool has_npos_v = has_npos<T>::value;

    The tests now run and pass.

  4. Add the following tests to the containsTest.cpp file:

    TEST_F(containsTest, DetectFind)

    {

        ASSERT_TRUE((has_find_v<std::string, char>));

        ASSERT_TRUE((has_find_v<std::set<int>, int>));

        ASSERT_FALSE((has_find_v<std::vector<int>, int>));

    }

    This test requires us to write a set of helper templates to detect if the container class has a find() method that takes one argument.

  5. Add the following code to the contains.hpp file:

    template <class T, class A0>

    auto test_find(int) ->

           decltype(void(std::declval<T>().find(std::declval<A0>())),

                                                            std::true_type{});

    template <class T, class A0>

    auto test_find(long) -> std::false_type;

    template <class T, class A0>

    struct has_find : decltype(test_find<T,A0>(0)) {};

    template< class T, class A0 >

    inline constexpr bool has_find_v = has_find<T, A0>::value;

    The tests now run and pass.

  6. Add the implementation for the generic container; in this case, the vector. Write the following tests in the containsTest.cpp file:

    TEST_F(containsTest, VectorContains)

    {

        std::vector<int> container {1,2,3,4,5};

        ASSERT_TRUE(contains(container, 5));

        ASSERT_FALSE(contains(container, 15));

    }

  7. Add the basic implementation of contains to the contains.hpp file:

    template<class C, class T>

    auto contains(const C& c, const T& key) -> decltype(std::end(c), true)

    {

            return std::end(c) != std::find(begin(c), end(c), key);

    }

    The tests now run and pass.

  8. The next step is to add the tests for the set special case to containsTest.cpp:

    TEST_F(containsTest, SetContains)

    {

        std::set<int> container {1,2,3,4,5};

        ASSERT_TRUE(contains(container, 5));

        ASSERT_FALSE(contains(container, 15));

    }

  9. The implementation of contains is updated to test for the built-in set::find() method:

    template<class C, class T>

    auto contains(const C& c, const T& key) -> decltype(std::end(c), true)

    {

        if constexpr(has_find_v<C, T>)

        {

            return std::end(c) != c.find(key);

        }

        else

        {

            return std::end(c) != std::find(begin(c), end(c), key);

        }

    }

    The tests now run and pass.

  10. Add the tests for the string special case to the containsTest.cpp file:

    TEST_F(containsTest, StringContains)

    {

        std::string container{"This is the message"};

        ASSERT_TRUE(contains(container, "the"));

        ASSERT_TRUE(contains(container, 'm'));

        ASSERT_FALSE(contains(container, "massage"));

        ASSERT_FALSE(contains(container, 'z'));

    }

  11. Add the following implementation of contains to test for the presence of npos and tailor the use of the find() method:

    template<class C, class T>

    auto contains(const C& c, const T& key) -> decltype(std::end(c), true)

    {

        if constexpr(has_npos_v<C>)

        {

            return C::npos != c.find(key);

        }

        else

        if constexpr(has_find_v<C, T>)

        {

            return std::end(c) != c.find(key);

        }

        else

        {

            return std::end(c) != std::find(begin(c), end(c), key);

        }

    }

    The tests now run and pass.

  12. Build and run the application called contains. Create a new Run Configuration. If your implementation of the contains template is correct, then the program will display the following output:
Figure 2B.36: Output from the successful implementation of contains
Figure 2B.36: Output from the successful implementation of contains

In this activity, we used various templating techniques in conjunction with SFINAE to select the appropriate implementation of a contains() function based upon the capability of the containing class. We could have achieved the same result using a generic template function and some specialized templates, but we took the path less travelled and flexed our newly found template skills.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime