Узнайте, ссылается ли std :: ostream & на глобальный объект - PullRequest
2 голосов
/ 05 апреля 2019

Если следующий метод

template<typename... Args>
void report(std::ostream&str, Args&&... args)
{
    (str << ... << args);
}

вызывается синхронно из разных потоков с одинаковым str, запись из разных потоков может быть искажена. Поэтому я использую вместо

template<typename... Args>
void report(std::ostream&str, Args&&... args)
{
    std::ostringstream ostr;
    (ostr << ... << args);
    str << ostr.str();
}

Теперь мне интересно, можно ли вместо этого определить, действительно ли это необходимо, т. Е.

{
    if(is_global_object(str)) {      // how to implement this?
        std::ostringstream ostr;
        (ostr << ... << args);
        str << ostr.str();
    } else
        (str << ... << args);
}

Вопрос: как реализовать is_global_object(str)? Я был бы счастлив обнаружить все std::cout, std::wcout, std::clog, std::wclog, std::cerr, std::wcerr.

Ответы [ 2 ]

3 голосов
/ 05 апреля 2019

Нет, точно не возможно.Что если один поток создаст локальный ostream и передаст ссылку на него другому потоку?Не глобальный, но все же общий.Глобальность объекта не определяет его местонахождение.

1 голос
/ 05 апреля 2019

Итак, во-первых, нет способа проверить, является ли какая-либо ссылка ссылкой на глобальный объект.По крайней мере, без некоторых жестких хаков для конкретной платформы (статическое сканирование памяти?), Которые могут не работать.Не иди по этой дороге.

Во-вторых (и это более важно) ваша проблема не является "глобальной".Это «общий».Почему вы думаете, что переменная должна быть глобальной, чтобы ее можно было использовать совместно?С другой стороны, могут быть глобальные переменные, которые не являются общими.Но в любом случае для этого также нет решения.Никакой хак вам здесь не поможет.

В-третьих, ваш средний код также не безопасен для потоков.Нет гарантии, что одиночная запись должна быть поточно-ориентированной.В некоторых случаях это (std :: cout), но нет такой гарантии для std :: ostream.Я могу легко написать свой собственный поток с записью без поточной защиты.

И последнее, но не менее важное: вы можете избежать всего этого, используя преимущества классов и мьютексов.Примерно так:

class Logger {
    public:
        Logger(std::ostream& stream): stream {stream}
        {};

        template<typename... Args>
        void log(Args&&... args)
        {
            std::lock_guard<std::mutex> lg {mu};
            (stream << ... << args);
        }

    private:
        std::ostream& stream;
        std::mutex mu;
};

и вообще не беспокойтесь обо всех нюансах безопасности потоков.

Для лучшего масштабирования вам может понадобиться взглянуть на некоторую очередь сообщений без блокировки,

...