5
\$\begingroup\$

Printing to stdout is thread-safe in many systems when using printf or std::cout, but not in all systems (Windows!). So I decided to make my own thread-safe and type-safe printing function with some help of C++11 variadic templates functions.

#include <iostream>
#include <iomanip>
#include <mutex>

class Mutex:private std::mutex{
public:
    class Lock:private std::lock_guard<std::mutex>{
    public:
        Lock(Mutex& mutex):
        std::lock_guard<std::mutex>(mutex){}
    };
};

class{
private:
    Mutex m_mutex;

    void Print(){}

    template<typename T,typename... Ts>
    void Print(const T& t,const Ts&... ts){
        std::cout<<t<<std::flush;
        Print(ts...);
    }

public:
    template<typename T,typename... Ts>
    void operator()(const T& t,const Ts&... ts){
        Mutex::Lock lock(m_mutex);
        Print(t,ts...);
    }
}Print;

template<typename T>
void PrintLine(){
    Print('\n');
}

template<typename T,typename... Ts>
void PrintLine(const T& t,const Ts&... ts){
    Print(t,ts...,'\n');
}

int main(){
    Print(10," devided by ",3," equals ");
    PrintLine(std::fixed,std::setprecision(5),10.0/3);
    return 0;
}
\$\endgroup\$
  • 1
    \$\begingroup\$ You may want to have a look at N3678 for another class designed to make output functions thread-safe :) \$\endgroup\$ – Morwenn May 24 '15 at 16:38
4
\$\begingroup\$

Mutex

class Mutex:private std::mutex{
  public:

Don't see the need for your custom Mutex/Lock class. Just use the standard ones directly there is no need to add a layer of indirection that future developers need to go and check.

Excessive Flushing

Calling flush after each item:

std::cout<<t<<std::flush;

Is probably not a good idea. Let the user of your code decide when to flush he probably has more concept about what what is about to be printed and thus when flush needs to be called.

If you must do it then do it after all the items have been printed.

Template Recusrsion.

Rather than using a recursive call to print all the items:

Print(ts...);

Use a sub class and create a std::initializer_list

// std::cout<<t<<std::flush;
// Print(ts...);
// Create a list. This will act more like a loop than recursion.
// Items inside the {} are initialized left to right.
auto printer = { ItemPrinter(ts)... };

Then ItemPrinter just prints the item in the constructor.

template<typename T>
struct ItemPrinter { ItemPrinter(T const& t){std::cout << t;}};

As a nice benifit. You will not need the terminating case.

void Print(){}

Naming Convention.

Print

It is more standard to use an initial capitol letter to define a type. An initial lower case letter donates an object (which also encompasses functions).

Since you have a nameless type. With an object called Print. I would have called it streamPrinter. Then used your wrapper functions call that directly.

Declaration

The code works well as is for a single file program. But you have problems when using it from header files. Because the class is nameless you can not use it any declarations to mark the object external and thus you will be getting an instantiation of the object in every compilation unit. This will break your guarantee that it is thread safe as each has its own mutex.

I think your best bet is to put the Print object in its own file. Then expose all printing via wrapper functions which can be made external in the header file.

Expansion.

Currently your printer is only used for std::cout. Why not expand it so that it can be used for any stream.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.