2

I have this sample block of code:

#include <iostream>
#include <iomanip>
#include <string>
using namespace std;

int main(){
    string blank = " ";
    cout << "Hello" << blank << "47"; 
}

I have a lot of cout's of this type in my original code. I want to be able to change the blank string to setw(2) function without having to replace blank with setw(2) on each and every cout I have in my code. So is there a way to set a cpp function to a variable? So I can call the function by typing the name? for example:

func blank = setw(2);
cout<< "Hello" << blank << "47";
Shayan
  • 709
  • 1
  • 15
  • 31
  • 3
    [`using namespace std;` is a bad practice](https://stackoverflow.com/q/1452721/2176813), never use it. – tambre Nov 12 '17 at 15:40
  • 1
    `#define blank std::setw(2)` ? – Killzone Kid Nov 12 '17 at 15:42
  • 2
    I don't understand why there are two votes to close this question because it is "unclear". How is it unclear? – Galik Nov 12 '17 at 15:47
  • *Fix-my-code* questions are off-topic, and teaching how to use an editor is off-topic on SO – Basile Starynkevitch Nov 12 '17 at 15:48
  • 1
    What KillzoneKid suggests definitely works, but the downside is all instances of identifier `blank` get replaced beforethe compiling starts (search term preprocessor) whether it makes sense to do so or not and the resulting error messages can be very obtuse. Unless you foresee multiple `blank`s in the future, just use the find and replace function found in practically any text editor. – user4581301 Nov 12 '17 at 15:50
  • 1
    @BasileStarynkevitch - And yet, you spent a considerable amount of time and effort to write just such an answer. So why reprimand the OP *now*? – StoryTeller - Unslander Monica Nov 12 '17 at 15:52
  • @user4581301 Hi, I tired and it didn't work, error said "expected an expression". – Shayan Nov 12 '17 at 16:06
  • 1
    That's because `#define` is utterly stupid. Every time `blank` is found in the code, it gets replaced. For example, `string blank = " ";` is turned into `string std::setw(2) = " ";` which clearly makes no sense and the compiler errors that result make no sense. I once had code that looked like `int strlen = somefunction();` and it took me the better part of a day to figure out that the `strlen` function was implemented as `#define strlen ...` and not a function in one of the toolchains I was using. – user4581301 Nov 12 '17 at 16:25
  • 1
    @Shayan `>> Hi, I tired and it didn't work, error said "expected an expression"` that is because you added `;` after the std::setw(2) – Killzone Kid Nov 12 '17 at 16:28
  • @KillzoneKid dude that's what I was looking for! can you add this as an answer so I can accept it? thanks – Shayan Nov 12 '17 at 16:32
  • @KillzoneKid: Using a macro is a horrible idea to begin with. Disguising it as a non-macro by using lowercase letters makes it even worse. – Christian Hackl Nov 12 '17 at 16:32
  • @Shayan You should probably use the answer provided by HolyBlackCat. As others pointed, #define maybe a quick solution but not the best. – Killzone Kid Nov 12 '17 at 16:34
  • Nah. What Shayan should be using is find-and-replace in their editor and not obfuscating their code needlessly. – user4581301 Nov 12 '17 at 16:48
  • @user4581301: Not all `std::setw(2)` in the OP's code may be equal. That becomes a problem when all `blank`s are replaced now but should be changed to something else later (e.g. `std::setw(3)`), without affecting the original `std::setw(2)`s. – Christian Hackl Nov 12 '17 at 17:03
  • For that case I'd have `cout<< "Hello" << std::setw(padding) << "47";` and `padding` sitting off as a constant elsewhere. If `blank` can later become `std::precision` or some other modifier, I'd say there's a rethink due. – user4581301 Nov 12 '17 at 17:20

3 Answers3

9

The type of std::setw(x) is unspecified, but you don't need to know it.

You can just use auto:

auto blank = std::setw(2);

As @StoryTeller noted, while this should work on sane implementations, it's not guaranteed to.

A safer option would be to make a class with overloaded <<:

struct blank_t {} blank;

std::ostream &operator<<(std::ostream &s, blank_t)
{
    return s << std::setw(2);
}
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • I'm trying to get more information on "auto" but I don't know what to search for, is "auto" a type? or an operator? guess both are wrong. – Shayan Nov 12 '17 at 15:50
  • 2
    `auto` is a request for the compiler to figure out the type for you. Documentation link: http://en.cppreference.com/w/cpp/language/auto – user4581301 Nov 12 '17 at 15:50
  • 1
    @Shayan - It's known formally as a [placeholder type specifier](http://en.cppreference.com/w/cpp/language/auto). – StoryTeller - Unslander Monica Nov 12 '17 at 15:51
  • 1
    This works and it's a neat trick, but it is a trick. And now anyone reading the code has to know or spend time figuring out the trick. That makes debugging trickier. Try to keep the as much of the context of the code in sight (a visible page or so) where possible. This would probably fail a code review without a good justification. In other words, can you do it? Yes. Should you do it? Probably not. – user4581301 Nov 12 '17 at 16:00
  • 3
    There's only one problem with this. Being unspecified, there is no guarantees the type will be copyable or movable. And even if it is, `operator <<` may be overloaded to accept only an rvalue reference. It's a bit brittle. – StoryTeller - Unslander Monica Nov 12 '17 at 16:01
  • 1
    @StoryTeller Right, I added a safer solution. – HolyBlackCat Nov 12 '17 at 17:43
  • @StoryTeller Since C++17 `auto blank = std::setw(2)` does not require the object to be copyable or movable. And prior to C++17, `auto&& blank` would overcome those problems. The rvalue objection is valid though. – M.M Dec 12 '17 at 23:41
3

std::setw is a manipulator. Its type is unspecified and implementation specific.

So is there a way to set a cpp function to a variable?

With C++11 you can use function objects, notably std::function. And you also have lambda expressions.

I am not sure you want to use it in your case.

So learn to use your source code editor. Replace every occurrence of blank with the appropriate stuff, that is std::setw(2) .... That makes your code more readable. Good editors are able to do that easily.

You could abuse the preprocessor, and have

#define blank setw(2)

but in your case that is a bad idea (because the code remains unreadable). Even using auto as answered by HolyBlackCat keeps your code unreadable and confusing.

Code is much more often read than written. Keep it readable (even by yourself, in a few weeks).

If you have a huge (million-line) project, spend perhaps a few minutes to write some script to change your source code in such case. BTW, with GNU emacs it is easy (since emacs is a scriptable editor).

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
1

An alternative solution is to write a small wrapper class whose only purpose is to provide an overloaded operator<< for an encapsulated referenced object. You can templatise that class so it works with everything that you could feed to an std::ostream in the first place.

Here is an example:

#include <iostream>
#include <iomanip>
#include <string>

template <class T>
struct Blank {
    Blank(T const& t) : t(t) {}
    T const& t;
};

// utility function so that you can use type deduction at the call site:
template <class T>
Blank<T> blank(T const& t) {
    return Blank<T>(t);
}

template <class T>
std::ostream& operator<<(std::ostream& os, Blank<T> const& blank) {
    os << std::setw(2) << blank.t;
    return os;
}

int main() {
    std::cout << "Hello" << blank("4") << blank(7) << blank(std::string("8")) << '\n';
}

It's not exactly the syntax you've asked for, but it comes pretty close.

You also have to make sure that no encapsulated object is destroyed before it's used in operator<< (because then you'd have undefined behaviour due to a dangling reference), but that's easy to accomplish if never create named Blank objects.

Christian Hackl
  • 27,051
  • 3
  • 32
  • 62