1

My question is: how to define a factory function that takes a parameter and returns a function pointer pointing to a function that is created according to that parameter? (or any other nice way to achieve the same result)


Explanation

In essence, I want to define a factory function set, which takes a std::string parameter str, and returns a pointer to function pf. The function pointer pf points to a function, just created in set, that takes std::ostream & and returns std::ostream &.

In this way, I want to call with std::cout << set(str) << "text" << std::endl;. I hope that the result of set(str) lives at least as long as this statement.

For your reference, it is from cplusplus that

ostream & operator<< (ostream & (*pf)(ostream &));


More Explanation

This is for a real example, shown as below.

First I have manipulators

std::ostream & black(std::ostream & os)
{
    return os << "\033[30m";            // make terminal text black
}

std::ostream & red(std::ostream & os)
{
    return os << "\033[31m";            // make terminal text red
}

std::ostream & green(std::ostream & os)
{
    return os << "\033[32m";            // make terminal text green
}

so that when I call

std::cout << red << "this text is in red" << std::endl;

I will have the desired effect. So far so good.

Now with a map

std::map<std::string, std::string> code =
{
    {"black", "30"},
    {"red", "31"},
    {"green", "32"}
    // ...
    // could extend much longer as there are many colors
};

I hope to achieve a similar, customized effect with foo("red") with

void foo(std::string str)
{
    std::cout << set(str) << ("this text is in " + str) << std::endl;
}

where set takes "red" and looks up the map code for the corresponding code "31".

But I have problems in implementing the set function. I would appreciate it much if anyone could help me with this!

Two notes:

  1. If possible, I want good performance for the set function, as it will be called again and again.

  2. Forgive me please if I think the wrong way --- So long as you can implement the functionality for set, I don't mind you do it in a different way.

Thank you for reading this long post. Thank you very much!

aafulei
  • 2,085
  • 12
  • 27

3 Answers3

3

You are thinking the wrong way about it. What you need to do instead is to create you own parameterized manipulator. Such manipulator can be implemented as a separate class for which you have to overload operator<< :

struct set_color
{
    std::string name;
    explicit set_color(std::string name)
        : name(std::move(name)) { }
    friend std::ostream & operator<<(std::ostream & os, const set_color &color)
    {
        if (color.name == "black")
            os << "\033[30m";
        else if ... // and so on
            ...
    }
}

You can use a map instead to convert name to color code, but the basic idea is that there is no need to create a bunch of functions for this.

r3mus n0x
  • 5,954
  • 1
  • 13
  • 34
  • Thank you for pointing out I was on the wrong way. I think yours are the standard, strightforward way to solving my question. But it looks like Marek R's solution directly kills my problem. – aafulei Jul 11 '19 at 14:54
2

Why make things complicated?

std::string_view color_from_name(std::string_view colorName)
{
    static const std::map<std::string_view, std::string_view> colors {
        {"red"sv, "\033[31m"sv},
        {"green"sv, "\033[32m"sv},
        {"black"sv, "\033[30m"sv},
    };
    return colors.at(colorName);
}

int main() {
    std::cout << color_from_name("red") << "tada" << color_from_name("green") << "got it\n";

    return 0;
}

https://wandbox.org/permlink/nh2qMKoovh2qVlk2

Marek R
  • 32,568
  • 6
  • 55
  • 140
  • This one has potential to be the solution. Will it work on case like `"bold_red_on_black"` where I want to parse the string and returns `"\033[1;31;40m"`? I plan to keep only 3 maps (instead of a very long one). One for style. One for text. One for background. – aafulei Jul 11 '19 at 14:36
  • Answer to myself: I think so. Then yeah, why did I make it this complicated? I do not need to return a manipulator at all. I simply write a function that takes a string and returns a string. – aafulei Jul 11 '19 at 14:48
  • I like this for simple modifiers. But I could imagine an API like `std::cout << color("red").bold().on("black")` with a class. – joshwilsonvu Jul 11 '19 at 14:54
  • @JoshWilson That's a good suggestion. Might be off the topic. But what if people cannot remember the order and some may want to call `std::cout << color("red").on("black").bold()`? For design purpose, I do not hope to introduce too much coding burden. – aafulei Jul 11 '19 at 14:58
  • Totally up to your design decision of course, but it's simple enough to make the order not matter. Just make a class with `on`, `bold` methods that set flags on the instance and `return *this`, then let the `operator<<` read the flags and produce the correct output. – joshwilsonvu Jul 11 '19 at 15:04
  • @JoshWilson You're right. I should have realized this before making a comment all too quickly. – aafulei Jul 11 '19 at 15:11
1

I think you could globally define another overload of operator<< as such:

using Manipulator = std::function<std::ostream&(std::ostream&)>;
std::ostream& operator<< (std::ostream& stream, Manipulator&>& func) {
    return func(stream);
}
std::map<std::string, Manipulator> manipulators;
for (const auto& val : data) {
    manipulators.emplace(std::pair<std::string, Manipulator>(val.first, 
            [&](std::ostream& out) -> std::ostream& { return out << "\033[" << val.second << "m"; }
    ));
}

If you stored a map of strings to lambda functions as your manipulators, you should be able to make your set() function a simple wrapper returning your lambda.

joshwilsonvu
  • 2,569
  • 9
  • 20
  • Thank you @Josh Wilson. This could be one way. But considering on a terminal there are 7 styles (like `bold`) and 16 text / background colors, there could be around `7 * 16 * 16 = 1792` combinations of stylized colors. It looks like you want to load all these into a `map` that maps to `std::function`. Good part is everything would be ready at the beginning. Bad thing may be it will take a while. Consider this will be library, and you don't always need all these colors. This is my personal take. – aafulei Jul 11 '19 at 14:23
  • Agreed, this would have a high setup cost. You could generate a new lambda function for each call to `set()`, and cache them for lookup next time. But @r3mux-n0x's answer is more idiomatic, and if you combined it with a [compile-type map](https://stackoverflow.com/a/26079954/7619380) of your colors you could eliminate setup time. – joshwilsonvu Jul 11 '19 at 14:33