Search code examples
c++c++17variadic-templatestemplate-meta-programmingfold-expression

How to detect availability of a parameter of given type in variadic funtion arguments list and act after all parameters have been processed


I have the following operator<<() overload for my class with C++17 folds:

template <typename... Args>
ostream& operator <<(Args&&... args)
{
    //Currently:
    return (m_osCout << ... << args);

    //What I need:
    IF ANY OF THE parameters in args "was" of type, say TSeek,  
    which can be a manipulator function etc,  then AFTER finishing 
    with the parameter pack, I would like to do some further operation
    , for instance, restore the state of m_osCount
 }

Is it possible what I need as described above? Any partial responses to set some directions would be appreciated...

Despite I coined the question as if I was asking for an automated stream flag restorer, please note that I am after the general solution, not particularly restoring std::cout or o/istream object restoration. In practice my class is a kind of mathematical object that accepts custom-types as operator arguments, some of which requires ostream's manipulators-like functions but it is generally very very inconvenient to require user to supply some finalizing operands before starting a next such usage.

One idea that came to me was to return a different kind of temporary object of a new smart type whenever TSeek was provided in args... list, so that after the last argument is forwarded to it, it will be destructed automatically and this really is the time that I want to do my finalizing task!

Should I proceed like this or...?


Solution

  • Well... as far I know, a stream operator<<() must receive exactly two arguments.

    So you can't define a variadic operator<<().

    If you accept a generic template variadic function, foo() by example, if you can use C++17 isn't really difficult.

    To check the presence of a type TSeek in Args... pack, you can write something as

    constexpr bool withTSeek { (std::is_same<Args, TSeek>{} || ...) };
    

    The following is a full compiling example

    #include <iostream>
    #include <utility>
    #include <type_traits>
    
    struct TSeek
     { };
    
    std::ostream & operator<< (std::ostream & o, TSeek const &)
     { return o << " [TSeek!] "; }
    
    template <typename ... Args>
    std::ostream & foo (std::ostream & o, Args && ... args)
     {
       constexpr bool withTSeek { (std::is_same<Args, TSeek>{} || ...) };
    
       (o << ... << args);
    
       if ( withTSeek )
          o << " --- TSeek detected" << std::endl;
       else 
          o << " --- TSeek NOT detected" << std::endl;
    
       return o;
     }
    
    int main ()
     {
       foo(std::cout, 1, 2, TSeek{}, 5, 7);
       foo(std::cout, 11, 13, 17, 19, 23);
     }