Jepessen
Jepessen

Reputation: 12415

Use << operator inside function arguments

I want to create a function that allows me to use the << operator in argument and inside it uses the corresponding stringstream.

Something like:

/* This does not do what i want below */
void printStream(std::stringstream& ss) {
  std::cout << ss.str();
}

/* Desired function usage */
printStream("The number is: " << 42 << ".");

What should I use in function declaration?

EDIT:

Following Deduplicator suggestion, I'm trying to use variadic template. For my purposes, I've decided to create a template that takes a list of argument and returns the composed string.

This is the code:

template <typename First, typename... Rest> void getStringInner(std::stringstream& ss, const First& first, const Rest&... rest) {
  ss << first;
  getStringInner(ss, rest...);
}

template <typename First, typename... Rest> std::string getString(const First& first, const Rest&... rest) {
  std::stringstream ss;
  getStringInner(ss, first, rest...);
  return ss.str();
}

Unfortunately Visual Studio gives me an error:

error C2678: binary '<<' : no operator found which takes a left-hand operand of type 'std::stringstream' (or there is no acceptable conversion) main.cpp 42 Test

The row with error is ss << first; in getStringInner.

What I'm doing wrong?

EDIT2:

I've found the error. This is the working code:

template <typename First> void getStringInner(std::stringstream& ss) {}

template <typename First> void getStringInner(std::stringstream& ss, const First& first) {
  ss << first;
}

template <typename First, typename... Rest> void getStringInner(std::stringstream& ss, const First& first, const Rest&... rest) {
  ss << first;
  getStringInner(ss, rest...);
}

template <typename First, typename... Rest> std::string getString(const First& first, const Rest&... rest) {
  std::stringstream ss;
  getStringInner(ss, first, rest...);
  return ss.str();
}

Upvotes: 3

Views: 187

Answers (5)

Emil Laine
Emil Laine

Reputation: 42828

You could use a macro:

#define printStream(arg) std::cout << arg

Thus when you write printStream("The number is: " << 42 << "."); it will expand to:

std::cout << "The number is: " << 42 << ".";

Which does what you wanted.

Upvotes: 1

iNyuu
iNyuu

Reputation: 72

what comes to my mind is creating your own string class, example Cl_CharArray, and to it add operator<< which would make a new Cl_CharArray from the strings left and right.

maybe using template will be needed. this task is fun, I will also do it sometime and return here. each class would then need a function to make it into a string (char array). I think at least one side (left or right from any operator) has to be a class, so it should be okay (that there is a number).

Upvotes: 0

Christian Hackl
Christian Hackl

Reputation: 27518

You will not be able to do this without changing the call site. "The number is: " << 42 is just a shift operation with a char[] and an int operand, regardless of the fact that the result of the expression is then supposed to be passed to some other function.

If you are willing to slightly change the calls, then you can use a proxy class that overloads operator<< and delegates the work to an std::ostringstream. Here is a basic example:

#include <iostream>
#include <sstream>
#include <string>

class Printer {
private:
  std::ostringstream oss;

public:

  Printer() {
  }

  std::string string() const {
      return oss.str();
  }

  template <class T>
  Printer& operator<<(T const& t) {
    oss << t;
    return *this;
  }
};

void printStream(Printer const& printer) {
  std::cout << printer.string();
}


int main() {
  printStream(Printer() << "The number is: " << 42 << ".");
}

Upvotes: 0

Deduplicator
Deduplicator

Reputation: 45654

Don't do that, it needs a preprocessor-hack.

Just use a variadic template and comma instead of left-shift:

template<class... X> void printStream(X&&... x) {
    std::stringstream ss;
    ((void)0, (void)(ss<<x), ...);
    std::cout << ss.rdbuf();
}

Upvotes: 2

6502
6502

Reputation: 114461

What you're looking for is difficult in C++ because the expression passed to a function will be computed independently; for example if a and b are integers there is no way to write a function or template foo so that

 foo(a << b);

does something different from calling foo passing a shifted by b (the compiler will look at a << b in isolation thus generating a bitshift operation).

What you can do is using the preprosessor, i.e. it's possible to have foo generating what you're looking for by writing a preprocessor macro.

#define foo(x)                \
   do{                        \
       std::ostream s_;       \
       s_ << x;               \
       std::cout << s_.str(); \
   } while(0)

this work by leveraging what is normally considered a limitation of the C preprocessor, i.e. that it works by textual substitution. The compiler in other words will be asked to compile s_ << a << b.

Upvotes: 1

Related Questions