Click here to Skip to main content
13,625,905 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

3.2K views
49 downloads
6 bookmarked
Posted 16 May 2018
Licenced CPOL

C++11: Compile-time Conditional Types

, 16 May 2018
Rate this:
Please Sign up or sign in to vote.
C++11's std::conditional tip with an endian swap example

Introduction

C++11 introduces std::conditional to give C++ developer the flexibility to choose a type based on the compile-time condition.

template< bool B, class T, class F >
struct conditional;

If the boolean parameter of the std::conditional is true, then the delved type is class T or else it is class F. Below is an example on how to use std::conditional. Before we use std::conditional, we have to include type_traits header. The typeinfo header is included because of typeid.

#include <iostream>
#include <type_traits>
#include <typeinfo>

int main() 
{
    typedef std::conditional<true, int, double>::type Type1;
    typedef std::conditional<false, int, double>::type Type2;
 
    std::cout << typeid(Type1).name() << '\n';
    std::cout << typeid(Type2).name() << '\n';
}

Output of the program is given below:

int
double

Applying to Endian Swapping

Let us apply what we have just learnt to implement endian swapping. We can determine whether to do endian swap at runtime. However, we can make that decision in compile time to get rid of the runtime check to stave off some execution time. The obvious disadvantage to this approach is we cannot change our decision during runtime. Let us delve into the code explanation. First, we define our Endian enum.

enum class Endian
{
    Big,
    Little
};
using BigEndian = std::integral_constant<Endian, Endian::Big>;
using LittleEndian = std::integral_constant<Endian, Endian::Little>;

The swap_endian_if_same_endian_is_false functions are defined below. The 2nd parameter determines whether the endian of the platform and data are the same. If it is false_type, then it must be swapped. We do nothing in the true_type case.

template<typename T>
void swap_endian_if_same_endian_is_false(T& val, std::false_type) // false_type means different endian, therefore must swap
{
    std::is_arithmetic<T> is_arithmetic_type;

    swap_endian_if_arithmetic(val, is_arithmetic_type);
}

template<typename T>
void swap_endian_if_same_endian_is_false(T& val, std::true_type)
{
    // same endian so do nothing.
}

We can determine the endian of the platform and data are the same with std::is_same. For this article, we are not going to bother using std::is_same. We short-circuit the check with std::false_type to force swapping.

using same_endian_type = std::is_same<BigEndian, LittleEndian>;

In the swap function mentioned above, we use is_arithmetic to check the type is integer or floating point before calling swap_endian_if_arithmetic. If T is not arithmetic, it is a no-op.

template<typename T>
void swap_endian_if_arithmetic(T& val, std::true_type)
{
    swap_endian(val, number_type<T>());
}

template<typename T>
void swap_endian_if_arithmetic(T& val, std::false_type)
{
    // T is not arithmetic so do nothing
}

These are the 5 overloaded swap_endian functions.

template<typename T>
void swap_endian(T& ui, UnknownSize)
{
}

template<typename T>
void swap_endian(T& ui, SizeOf1)
{
}

template<typename T>
void swap_endian(T& ui, SizeOf8)
{
    union EightBytes
    {
        T ui;
        uint8_t arr[8];
    };

    EightBytes fb;
    fb.ui = ui;
    // swap the endian
    std::swap(fb.arr[0], fb.arr[7]);
    std::swap(fb.arr[1], fb.arr[6]);
    std::swap(fb.arr[2], fb.arr[5]);
    std::swap(fb.arr[3], fb.arr[4]);

    ui = fb.ui;
}

template<typename T>
void swap_endian(T& ui, SizeOf4)
{
    union FourBytes
    {
        T ui;
        uint8_t arr[4];
    };

    FourBytes fb;
    fb.ui = ui;
    // swap the endian
    std::swap(fb.arr[0], fb.arr[3]);
    std::swap(fb.arr[1], fb.arr[2]);

    ui = fb.ui;
}

template<typename T>
void swap_endian(T& ui, SizeOf2)
{
    union TwoBytes
    {
        T ui;
        uint8_t arr[2];
    };

    TwoBytes fb;
    fb.ui = ui;
    // swap the endian
    std::swap(fb.arr[0], fb.arr[1]);

    ui = fb.ui;
}

Which swap_endian function is selected by C++ compiler is determined by the 2nd parameter type which is empty structure with a default constructor.

struct SizeOf1 { SizeOf1() { std::cout << "Size:1" << std::endl; } };
struct SizeOf2 { SizeOf2() { std::cout << "Size:2" << std::endl; } };
struct SizeOf4 { SizeOf4() { std::cout << "Size:4" << std::endl; } };
struct SizeOf8 { SizeOf8() { std::cout << "Size:8" << std::endl; } };
struct UnknownSize { UnknownSize() { std::cout << "Size:Unknown" << std::endl; } };

number_type is alias template which makes use of nested std::conditional to determine the type to be SizeOf1, SizeOf2, SizeOf4, SizeOf8 or UnknownSize based on the sizeof(T). sizeof(T) is always evaluated at compile time.

template <class T>
using number_type =
typename std::conditional<
    sizeof(T) == 1,
    SizeOf1,
    typename std::conditional<
    sizeof(T) == 2,
    SizeOf2,
    typename std::conditional<
    sizeof(T) == 4,
    SizeOf4,
    typename std::conditional<
    sizeof(T) == 8,
    SizeOf8,
    UnknownSize
    >::type
    >::type
    >::type
>::type;

Here is an example of swapping the integer twice. std::false_type is specified for the 2nd argument to force swapping.

int main()
{
    int num = 1;
    std::cout << num << std::endl;
    swap_endian_if_same_endian_is_false(num, std::false_type());
    std::cout << num << std::endl;
    swap_endian_if_same_endian_is_false(num, std::false_type());
    std::cout << num << std::endl;

    return 0;
}

The output of the main function is shown:

1
Size:4
16777216
Size:4
1

std::conditional is a useful tool we can add to our arsenal when used judiciously. The repository for this article is at Github.

References

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Cake Processor
Software Developer (Senior)
United States United States
C++ developer transitioning to Python

Right now, I am picking up DevOps skills at Pluralsight and pursuing CCNA certification. Stay tuned for my CCNA related article!

Coding Tidbit Blog

Latest blogpost: C++ – The Forgotten Trojan Horse by Eric Johnson

Yinyang Theory Blog

If you like unconventional wisdom based on Yinyang philosophy, hop over to read my Yinyang blog.

Latest blogpost: Fortune Telling: Good News or Bad News For You?

IT Certifications

  • IT Infrastructure Library Foundational (ITIL v3)
  • Scrum Alliance Certified Scrum Master (CSM)
  • Certified Secure Software Lifecycle Professional (CSSLP)

View my certificates here.

You may also be interested in...

Pro

Comments and Discussions

 
GeneralThe swap_endian template is much more work than it needs to be Pin
John M. Dlugosz28-May-18 11:02
memberJohn M. Dlugosz28-May-18 11:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web03-2016 | 2.8.180712.1 | Last Updated 16 May 2018
Article Copyright 2018 by Cake Processor
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid