Re: Как наиболее корректно работать с глобальными данными
От: Шахтер Интернет  
Дата: 03.07.15 09:41
Оценка: +4 -1
Здравствуйте, lnkuser, Вы писали:

L>Везде, абсолютно везде пишут что глобальные переменные, объекты это плохо и очень плохо.


Это глупость. Например, хип есть глобальный объект и ты им (как правило неявно) пользуешься в большинстве программ.
То же самое относится к целому ряду объектов, например стандартные потоки ввода-вывода в консольных приложениях.

L>Но нигде нормального ответа как обходится без них я не нашел. На форуме искал.


А не нужно обходится без них. Не надо заниматься мазохизмом.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re: Как наиболее корректно работать с глобальными данными
От: Хон Гиль Дон Россия  
Дата: 03.07.15 11:04
Оценка: +4 -1
Здравствуйте, lnkuser, Вы писали:

L>Везде, абсолютно везде пишут что глобальные переменные, объекты это плохо и очень плохо.


Это часто пишут люди, которые не вполне разобрались в трудах классиков, зато расширили и углубили мысль. А классики сначала ругали исключительно глобальные переменные. Да, использование глобальных переменных в стиле говнокода 80-х (довелось несколько программ портировать) — это очень плохо. Но это крайне редко встречающийся сейчас случай. Растет культурка программирования-то.

L>Но нигде нормального ответа как обходится без них я не нашел. На форуме искал.


Да нормально с ними все, глобальные сущности удобно выражать с помощью глобальных объектов. Не, можно конечно заранее пытаться предусмотреть неограниченную гибкость и расширяемость, но это будут гигантские усилия без отдачи.

L>Допустим есть класс Log, который инициализируется при старте программы гдето в самом начале main().

L>И есть другие части программы которые работают в разных потоках, там тоже используется Log.

L>Как я понял это как раз случай для синглетона:


L>
L>// other_module.cpp

L>auto g_log = Log::instance();
L>*g_log << loglevel::info << "some text";
L>




L>
L>// other_module2.cpp

L>auto g_log = Log::instance();
L>*g_log << loglevel::info << "some text2";
L>



Можно и синглетон, но обычно удобнее просто свободные функции.

L>Все вроде идеально, но тут будет ошибка линкования.


С фигов ли, если писать правильно?


L>Другой рабочий вариант это через extern:


L>но опять таки, много где пишут мол старайтесь избегать extern.


Да можно и экстерн, но это ж потроха наружу. Чуть что поменял — и вся программа перекомпилируется. Фтопку.

L> А что использовать взамен тогда???

L>Как правильно спроектированные приложения решают проблему глобальных данных?

Функции. Хип в пример уже приводили — делайте так же.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re: Как наиболее корректно работать с глобальными данными
От: Abyx Россия  
Дата: 03.07.15 10:50
Оценка: -2
Здравствуйте, lnkuser, Вы писали:

Почитайте про Dependency Injection.
Один из примеров — это Boost.DI https://github.com/krzysztof-jusiak/di
In Zen We Trust
Re: Как наиболее корректно работать с глобальными данными
От: Don Reba Канада https://stackoverflow.com/users/49329/don-reba
Дата: 03.07.15 12:51
Оценка: +2
Здравствуйте, lnkuser, Вы писали:

L>Как я понял это как раз случай для синглетона:


Синглетоны — это те же глобальные объекты и часто считаются антипаттерном.
Ce n'est que pour vous dire ce que je vous dis.
Re: Как наиболее корректно работать с глобальными данными
От: MasterZiv СССР  
Дата: 07.07.15 08:07
Оценка: +2
Здравствуйте, lnkuser, Вы писали:

Отвечу в двух словах:

L>Везде, абсолютно везде пишут что глобальные переменные, объекты это плохо и очень плохо.


Врут. Нагло и беспардонно врут. Глобальные переменные -- это не плохо или хорошо, а просто глобальные
переменные, у них есть свои особенности, их надо знать, и уметь ими пользоваться.

L>Но нигде нормального ответа как обходится без них я не нашел. На форуме искал.


Без них ненадо обходиться. Их надо использовать, но использовать их надо правильно.
В частности, если испольуешь их в разных потоках, доступ к ним нужно защищать.
Если тебе нужны "как бы глобальные" переменные, но для отдельно взятого потока -- использовать
TLS (thread local storage) и его аналоги.
Re[2]: Как наиболее корректно работать с глобальными данными
От: Abyx Россия  
Дата: 03.07.15 10:48
Оценка: -1
Здравствуйте, Шахтер, Вы писали:

Ш>Это глупость. Например, хип есть глобальный объект и ты им (как правило неявно) пользуешься в большинстве программ.

во многих библиотеках делают возможность задать свои функции аллокации/деаллокации, и хип напрямую не используется.

Ш>То же самое относится к целому ряду объектов, например стандартные потоки ввода-вывода в консольных приложениях.

тожесамое. если где-то так написано, ты не можешь по-нормальному реюзнуть такой код в GUI программе.
In Zen We Trust
Re: Как наиболее корректно работать с глобальными данными
От: Evgeny.Panasyuk Россия  
Дата: 03.07.15 13:03
Оценка: +1
Здравствуйте, lnkuser, Вы писали:

L>Везде, абсолютно везде пишут что глобальные переменные, объекты это плохо и очень плохо.


А ещё пишут что goto и friend это плохо. Некоторые даже считают что обычные функции это плохо, и всё должно быть классом/объектом
Я практически для всего что фанатики называют considered harmful видел достойные применения.

L>Но нигде нормального ответа как обходится без них я не нашел. На форуме искал.


Если пытаться без них совсем по-честному, то нужно в каждую функцию использующую глобальное состояние передавать дополнительные параметры — плюсом здесь является чуть более простая кастомизация. Но в случае логирования это редко оправданно.
Как правило же используют те же самые глобальные объекты, но замаскированные под какими-нибудь buzzwords типа ServiceLocator или IoC-Container.

L>Все вроде идеально, но тут будет ошибка линкования.


Тема хорошо раскрыта в Modern C++ Design Александреску.
Один из простых вариантов — это так называемый Meyers Singleton:
// in header:
inline Log &log()
{
    static Log x;
    return x;
}
Отредактировано 03.07.2015 13:05 Evgeny.Panasyuk . Предыдущая версия .
Re[6]: Как наиболее корректно работать с глобальными данными
От: VTT http://vtt.to
Дата: 08.07.15 19:40
Оценка: :)
Здравствуйте, enji, Вы писали:

E>удобно


Для меня это последний аргумент.
C++ это вообще не особо удобный язык: предварительное объявление всего и вся, крякозябры, экнмнабквх, неконсистентность, костыли, скудность средств "из коробки" и т.д.
Однако, по сравнению с C, в нем имеется и ряд прогрессивных нововведений, позволяющих защититься от множества непреднамеренных ошибок, повысить выразительность (и эффективность) языка. И наличие директив контроля доступа (пусть и в таком недоделанном виде) я считаю одним из самых полезных.
С другой стороны, плюрализма C тоже никто не отменял, и пользоваться всеми этими дополнительными средствами вроде как никто не заставляет.
Но каждый раз, когда мне хочется срезать угол, забить на контроль доступа, const-корректность, или даже просто назвать переменную покороче i вместо полноценного item_index, у меня возникают смутные подозрения, что я скатываюсь в трясину говнокода и этот срезанный угол мне когда-нибудь аукнется. Разумеется, использование этих средств вовсе не гарантирует отсутствие проблем в коде, но вот их неиспользование наличие проблем гарантирует.

И да, я скажу что C-шный трехсекционный for(;;) не нужен, C-style cast не нужен, С-style enum не нужен, объявление переменных через запятую не нужно, assignment chaining не нужен, malloc не нужен, ..., вымирание панд не нужно. Только вот все эти безобразия продолжат существовать несмотря на мое негодование.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Отредактировано 08.07.2015 19:43 VTT . Предыдущая версия . Еще …
Отредактировано 08.07.2015 19:41 VTT . Предыдущая версия .
Как наиболее корректно работать с глобальными данными
От: lnkuser  
Дата: 03.07.15 08:36
Оценка:
Везде, абсолютно везде пишут что глобальные переменные, объекты это плохо и очень плохо.
Но нигде нормального ответа как обходится без них я не нашел. На форуме искал.

Допустим есть класс Log, который инициализируется при старте программы гдето в самом начале main().
И есть другие части программы которые работают в разных потоках, там тоже используется Log.

Как я понял это как раз случай для синглетона:

// other_module.cpp

auto g_log = Log::instance();
*g_log << loglevel::info << "some text";




// other_module2.cpp

auto g_log = Log::instance();
*g_log << loglevel::info << "some text2";



Все вроде идеально, но тут будет ошибка линкования.


Другой рабочий вариант это через extern:

// main.hpp

std::shared_ptr<Log> g_log = nullptr;



// main.cpp

#include "main.hpp"

g_log = std::make_shared<Log>();


// other_module.cpp

extern std::shared_ptr<Log> log;



но опять таки, много где пишут мол старайтесь избегать extern. А что использовать взамен тогда???
Как правильно спроектированные приложения решают проблему глобальных данных?


Какие книги есть по данной тематике (а именно физический дизайн приложения), где бы описывалось как правильно строить программу.


Спасибо
Re: Как наиболее корректно работать с глобальными данными
От: koenjihyakkei Россия  
Дата: 03.07.15 09:43
Оценка:
Так например:

*Log::instance() << loglevel::info << "some text";

Или можно в файле, где определен Log, для удобства сделать дефайн типа:

#define LOG *Log::instance()

тогда

LOG << loglevel::info << "some text";

И не понятно, зачем вообще в каждом файле создавать переменную для лога
Отредактировано 03.07.2015 9:46 koenjihyakkei . Предыдущая версия .
Re[3]: Как наиболее корректно работать с глобальными данными
От: Evgeny.Panasyuk Россия  
Дата: 03.07.15 12:38
Оценка:
Здравствуйте, Abyx, Вы писали:

Ш>>То же самое относится к целому ряду объектов, например стандартные потоки ввода-вывода в консольных приложениях.

A>тожесамое. если где-то так написано, ты не можешь по-нормальному реюзнуть такой код в GUI программе.

Стандартные потоки можно перенаправить куда угодно через rdbuf, хоть в тот же GUI.
Re[2]: Как наиболее корректно работать с глобальными данными
От: Ops Россия  
Дата: 03.07.15 13:32
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Один из простых вариантов — это так называемый Meyers Singleton:

EP>
EP>// in header:
EP>inline Log &log()
EP>{
EP>    static Log x;
EP>    return x;
EP>}
EP>


Однако ж грабли в VC до Nov 2013 CTP.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Re[3]: Как наиболее корректно работать с глобальными данными
От: Evgeny.Panasyuk Россия  
Дата: 03.07.15 13:36
Оценка:
Здравствуйте, Ops, Вы писали:

EP>>Один из простых вариантов — это так называемый Meyers Singleton:

EP>>
EP>>// in header:
EP>>inline Log &log()
EP>>{
EP>>    static Log x;
EP>>    return x;
EP>>}
EP>>

Ops>Однако ж грабли в VC до Nov 2013 CTP.

Ты о thread-safe initialization? Так она вполть до C++11 не гарантировалась, а как это обходится — есть в упомянутой книге.
Отредактировано 03.07.2015 13:36 Evgeny.Panasyuk . Предыдущая версия .
Re[3]: Как наиболее корректно работать с глобальными данными
От: Шахтер Интернет  
Дата: 03.07.15 13:48
Оценка:
Здравствуйте, Abyx, Вы писали:

A>Здравствуйте, Шахтер, Вы писали:


Ш>>Это глупость. Например, хип есть глобальный объект и ты им (как правило неявно) пользуешься в большинстве программ.

A>во многих библиотеках делают возможность задать свои функции аллокации/деаллокации, и хип напрямую не используется.

И что, хип после этого перестаёт быть глобальным объектом?
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[3]: Как наиболее корректно работать с глобальными данными
От: Хон Гиль Дон Россия  
Дата: 03.07.15 14:00
Оценка:
Здравствуйте, Abyx, Вы писали:


Ш>>Это глупость. Например, хип есть глобальный объект и ты им (как правило неявно) пользуешься в большинстве программ.

A>во многих библиотеках делают возможность задать свои функции аллокации/деаллокации, и хип напрямую не используется.

Ага, одна пара маллок/фри на всю либу. То есть глобальные. Последовательного применения dependency injection, шоп например в каждый объект тащить аллокатор, логгер и конфиг, я в реальном вменяемом коде ни разу пока не видел. Не, я допускаю, что области где они сплошь требуются, бывают, но советовать это по умолчанию по-моему перебор.

Ш>>То же самое относится к целому ряду объектов, например стандартные потоки ввода-вывода в консольных приложениях.

A>тожесамое. если где-то так написано, ты не можешь по-нормальному реюзнуть такой код в GUI программе.

Чтобы с этим не было проблем, достаточно обеспечить подключаемые реализации глобальных объектов с разумными умолчаниями. Например, логгер никуда не выводит, аллокатор вызывает CRT'шные malloc/free, конфиг возвращает какие-то дефолты. А DI уместнее оставить для случаев, где неявных границ нет, а разное поведение требуется.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re: Как наиболее корректно работать с глобальными данными
От: vpchelko  
Дата: 03.07.15 19:24
Оценка:
Здравствуйте, lnkuser, Вы писали:

L>Везде, абсолютно везде пишут что глобальные переменные, объекты это плохо и очень плохо.

L>Но нигде нормального ответа как обходится без них я не нашел. На форуме искал.

Для маленькой утилиты вообще пофиг.
А когда проект разрастается, глобальные переменные вызывают только головную боль. Особенно при разрушении глобальных переменных.

L>Допустим есть класс Log, который инициализируется при старте программы гдето в самом начале main().

L>И есть другие части программы которые работают в разных потоках, там тоже используется Log.

Если твой логер будет использоваться в деструкторе глобальных объектов — будет больно.

L>Как я понял это как раз случай для синглетона:

L>но опять таки, много где пишут мол старайтесь избегать extern. А что использовать взамен тогда???
L>Как правильно спроектированные приложения решают проблему глобальных данных?

Избегать в больших проектах, ибо потом заманаешься фантомные баги фиксить.

А также большие проблемы с доступом к глобальным переменным. Какие гарантии thread safe? Сильно мешает отладке.

И любой чих — может положить все приложение.

Например в хроме, всякие там дескрипторы хранятся в глобальных мапах. И как последствие регулярные баги связные с этим приколом.

L>Какие книги есть по данной тематике (а именно физический дизайн приложения), где бы описывалось как правильно строить программу.


L>Спасибо
Сало Украине, Героям Сала
Отредактировано 03.07.2015 19:40 vpchelko . Предыдущая версия . Еще …
Отредактировано 03.07.2015 19:31 vpchelko . Предыдущая версия .
Отредактировано 03.07.2015 19:28 vpchelko . Предыдущая версия .
Отредактировано 03.07.2015 19:25 vpchelko . Предыдущая версия .
Re[4]: Как наиболее корректно работать с глобальными данными
От: Abyx Россия  
Дата: 03.07.15 20:16
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

Ш>>>То же самое относится к целому ряду объектов, например стандартные потоки ввода-вывода в консольных приложениях.

A>>тожесамое. если где-то так написано, ты не можешь по-нормальному реюзнуть такой код в GUI программе.

EP>Стандартные потоки можно перенаправить куда угодно через rdbuf, хоть в тот же GUI.


но только глобально для всей программы.
In Zen We Trust
Re[4]: Как наиболее корректно работать с глобальными данными
От: Abyx Россия  
Дата: 03.07.15 20:23
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>>>Это глупость. Например, хип есть глобальный объект и ты им (как правило неявно) пользуешься в большинстве программ.

A>>во многих библиотеках делают возможность задать свои функции аллокации/деаллокации, и хип напрямую не используется.

Ш>И что, хип после этого перестаёт быть глобальным объектом?


да, перестает.
в той же винде можно создать несколько разных хипов для разных потоков или модулей программы.
или например можно использовать arena allocator'ы.
In Zen We Trust
Re[4]: Как наиболее корректно работать с глобальными данными
От: Abyx Россия  
Дата: 03.07.15 20:57
Оценка:
Здравствуйте, Хон Гиль Дон, Вы писали:

ХГД>Здравствуйте, Abyx, Вы писали:



Ш>>>Это глупость. Например, хип есть глобальный объект и ты им (как правило неявно) пользуешься в большинстве программ.

A>>во многих библиотеках делают возможность задать свои функции аллокации/деаллокации, и хип напрямую не используется.

ХГД>Ага, одна пара маллок/фри на всю либу. То есть глобальные. Последовательного применения dependency injection, шоп например в каждый объект тащить аллокатор, логгер и конфиг, я в реальном вменяемом коде ни разу пока не видел. Не, я допускаю, что области где они сплошь требуются, бывают, но советовать это по умолчанию по-моему перебор.


ну вот есть 7zip, там так и сделано.
если бы оттуда COM выпилить — был бы совсем хороший код %)

а вообще если использовать что-то типа DI-контейнера/сервис-локатора, то код получается довольно простым.
using MyDIContainer = std::tuple<Logger*, Config*, ...>;

class Foo {
public:
  Foo(const MyDIContainer& di_container) {
    auto cfg = get<Config*>(di_container); // std::get
    ...
    log(di_container, "something", 123); // calls "if (auto logger = get<Logger*>(di_container)) logger->log(args...);"
    ...
    auto bar = allocate<Bar>(di_container, 456); // calls "get<Allocator*>(di_container)->Create<T>(args...);"
  };
};


если сделать вспомогательный базовый класс, часть бойлерплейта можно убрать.

Ш>>>То же самое относится к целому ряду объектов, например стандартные потоки ввода-вывода в консольных приложениях.

A>>тожесамое. если где-то так написано, ты не можешь по-нормальному реюзнуть такой код в GUI программе.

ХГД>Чтобы с этим не было проблем, достаточно обеспечить подключаемые реализации глобальных объектов с разумными умолчаниями. Например, логгер никуда не выводит, аллокатор вызывает CRT'шные malloc/free, конфиг возвращает какие-то дефолты. А DI уместнее оставить для случаев, где неявных границ нет, а разное поведение требуется.


Всё это хорошо до тех пор, пока тебе не понадобится например запустить две функции параллельно, и дать каждой свой логгер.
Или вызвать void foo(function<void()> delegate); так чтобы код в foo писал в логгер, а delegate туда не писал.
Можно конечно запилить демультиплексор, но все же лучше иметь возможность задавать свои логи.
In Zen We Trust
Re[5]: Как наиболее корректно работать с глобальными данными
От: Evgeny.Panasyuk Россия  
Дата: 03.07.15 23:09
Оценка:
Здравствуйте, Abyx, Вы писали:

Ш>>>>То же самое относится к целому ряду объектов, например стандартные потоки ввода-вывода в консольных приложениях.

A>>>тожесамое. если где-то так написано, ты не можешь по-нормальному реюзнуть такой код в GUI программе.
EP>>Стандартные потоки можно перенаправить куда угодно через rdbuf, хоть в тот же GUI.
A>но только глобально для всей программы.

При необходимости не трудно сделать scope/region-based — то есть guard'ы позволят использовать разные логгеры на разных отрезках callstack. Плюс можно сделать специализацию по потокам через TLS или thread_id.

Обычно же одного логгера достаточно на всё приложение. Если конечно требуется управлять несколькими потоками логирования, или вообще задача в том чтобы аггрегировать и перенаправлять логи — то тут естественно они нужны в явном виде.
Re[5]: Как наиболее корректно работать с глобальными данными
От: Evgeny.Panasyuk Россия  
Дата: 03.07.15 23:42
Оценка:
Здравствуйте, Abyx, Вы писали:

A>а вообще если использовать что-то типа DI-контейнера/сервис-локатора, то код получается довольно простым.

A>
A>using MyDIContainer = std::tuple<Logger*, Config*, ...>;

A>class Foo {
A>public:
A>  Foo(const MyDIContainer& di_container) {
A>    auto cfg = get<Config*>(di_container); // std::get
A>    ...
A>    log(di_container, "something", 123); // calls "if (auto logger = get<Logger*>(di_container)) logger->log(args...);"
A>    ...
A>    auto bar = allocate<Bar>(di_container, 456); // calls "get<Allocator*>(di_container)->Create<T>(args...);"
A>  };
A>};
A>


У тебя здесь обычный внешний параметр, пусть и составной.

IoC/DI-контейнер же это некоторый объект, как правило глобальный, который умеет создавать объекты требующие зависимости. Сами зависимые классы его не видят, и о нём ничего не знают.
Конкретные зависимости выбираются в соответствии с настроенными правилами — например можно указать что объекту Bar всегда давать логгер типа LoggerBar, или например внутри каждого потока (или даже соединения) использовать отдельный логгер.

Вот первый попавшийся пример (с кучей опечаток ):
public interface IWeapon  
{  
    void sord();  
}  

public class Ninja : IWeapon  
{  
    public void sord()  
    {  
        Console.WriteLine("I am using Sord");  
    }  
}  

public class sourav  
{  
    IWeapon ObjWeapon = null;  
    public sourav(IWeapon tmpWeapon)  
    {  
        this.ObjWeapon = tmpWeapon;  
    }  

    public void Attack()  
    {  
        this.ObjWeapon.sord();  
    }  
}  

class Program  
{  
    static void Main(string[] args)  
    {  
        Ninject.IKernel kernal = new StandardKernel();  
        kernal.Bind<IWeapon>().To<Ninja>();  
        var instance = kernal.Get<sourav>();  
        instance.Attack();   
        Console.ReadLine();  
    }  
}
Отредактировано 03.07.2015 23:44 Evgeny.Panasyuk . Предыдущая версия .
Re[2]: Как наиболее корректно работать с глобальными данными
От: lnkuser  
Дата: 04.07.15 06:52
Оценка:
A>Почитайте про Dependency Injection.
A>Один из примеров — это Boost.DI https://github.com/krzysztof-jusiak/di

Спасибо, буду разбираться.
Re[5]: Как наиболее корректно работать с глобальными данными
От: Хон Гиль Дон Россия  
Дата: 04.07.15 13:54
Оценка:
Здравствуйте, Abyx, Вы писали:


ХГД>>Ага, одна пара маллок/фри на всю либу. То есть глобальные. Последовательного применения dependency injection, шоп например в каждый объект тащить аллокатор, логгер и конфиг, я в реальном вменяемом коде ни разу пока не видел. Не, я допускаю, что области где они сплошь требуются, бывают, но советовать это по умолчанию по-моему перебор.


A>ну вот есть 7zip, там так и сделано.


В котором месте? Чето не припомню там ни стороннего аллокатора, ни тем более логгера. Хотя давно я туда не лазил, мог и забыть.

A>если бы оттуда COM выпилить — был бы совсем хороший код %)


COMа там нет вообще, просто чувак писал в стиле как будто ком есть
А вообще за такой хороший код руки отрывать, диагностика на 3 с минусом. Ежели какая ошибка случилась — в вызывающем коде причину фиг узнаешь.

A>а вообще если использовать что-то типа DI-контейнера/сервис-локатора, то код получается довольно простым.

A>
A>using MyDIContainer = std::tuple<Logger*, Config*, ...>;

A>class Foo {
A>public:
A>  Foo(const MyDIContainer& di_container) {
A>    auto cfg = get<Config*>(di_container); // std::get
A>    ...
A>    log(di_container, "something", 123); // calls "if (auto logger = get<Logger*>(di_container)) logger->log(args...);"
A>    ...
A>    auto bar = allocate<Bar>(di_container, 456); // calls "get<Allocator*>(di_container)->Create<T>(args...);"
A>  };
A>};
A>


A>если сделать вспомогательный базовый класс, часть бойлерплейта можно убрать.


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

ХГД>>Чтобы с этим не было проблем, достаточно обеспечить подключаемые реализации глобальных объектов с разумными умолчаниями. Например, логгер никуда не выводит, аллокатор вызывает CRT'шные malloc/free, конфиг возвращает какие-то дефолты. А DI уместнее оставить для случаев, где неявных границ нет, а разное поведение требуется.


A>Всё это хорошо до тех пор, пока тебе не понадобится например запустить две функции параллельно, и дать каждой свой логгер.


Вот ни разу такого изврата не требовалось. И нафига, спрашивается, я буду платить за все это ненужное счастье с самого начала?

A>Или вызвать void foo(function<void()> delegate); так чтобы код в foo писал в логгер, а delegate туда не писал.


Вот когда мне такое потребуется, я приделаю к этой конкретной функции параметр, писать в логгер или нет. И это будет на порядок проще и потребует на 2 порядка меньше работы, чем DI заранее сверху донизу.

A>Можно конечно запилить демультиплексор, но все же лучше иметь возможность задавать свои логи.


Чем лучше-то? Если я ей пользоваться, скорее всего, ни разу не буду?
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[6]: Как наиболее корректно работать с глобальными данными
От: BulatZiganshin  
Дата: 04.07.15 17:01
Оценка:
Здравствуйте, Хон Гиль Дон, Вы писали:

ХГД>>>Ага, одна пара маллок/фри на всю либу. То есть глобальные. Последовательного применения dependency injection, шоп например в каждый объект тащить аллокатор, логгер и конфиг, я в реальном вменяемом коде ни разу пока не видел. Не, я допускаю, что области где они сплошь требуются, бывают, но советовать это по умолчанию по-моему перебор.


A>>ну вот есть 7zip, там так и сделано.


typedef struct
{
void *(*Alloc)(void *p, size_t size);
void (*Free)(void *p, void *address); /* address can be 0 */
} ISzAlloc;
Люди, я люблю вас! Будьте бдительны!!!
Re[7]: Как наиболее корректно работать с глобальными данными
От: Хон Гиль Дон Россия  
Дата: 04.07.15 17:19
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:


ХГД>>>>Ага, одна пара маллок/фри на всю либу. То есть глобальные. Последовательного применения dependency injection, шоп например в каждый объект тащить аллокатор, логгер и конфиг, я в реальном вменяемом коде ни разу пока не видел. Не, я допускаю, что области где они сплошь требуются, бывают, но советовать это по умолчанию по-моему перебор.


A>>>ну вот есть 7zip, там так и сделано.


BZ>typedef struct

BZ>{
BZ> void *(*Alloc)(void *p, size_t size);
BZ> void (*Free)(void *p, void *address); /* address can be 0 */
BZ>} ISzAlloc;

ISzAlloc упоминается в исходниках аж 42 раза. new употребляется более 300 раз. 42 раза, конечно, больше чем ничего, но на повсеместное использование не тянет. Т.е., свой аллокатор там, судя по всему, в тех местах, где автор подозревал влияние его на производительность. Что несколько отличается от применения DI по умолчанию по всему коду.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[8]: Как наиболее корректно работать с глобальными данными
От: BulatZiganshin  
Дата: 04.07.15 17:30
Оценка:
Здравствуйте, Хон Гиль Дон, Вы писали:

ХГД>ISzAlloc упоминается в исходниках аж 42 раза. new употребляется более 300 раз. 42 раза, конечно, больше чем ничего, но на повсеместное использование не тянет. Т.е., свой аллокатор там, судя по всему, в тех местах, где автор подозревал влияние его на производительность. Что несколько отличается от применения DI по умолчанию по всему коду.


сравнивать new надо с Alloc, а не названием типа. для интереса поискал "->Alloc(" в исходниках 11-го года, там оно втречается раз 20, в алгоритмах сжатия и при разборе оглавления архива — т.е. только в тех местах, где могут потребоваться очень большие буфера и потому хитрая стратегия переиспользования памяти
Люди, я люблю вас! Будьте бдительны!!!
Re[2]: Как наиболее корректно работать с глобальными данными
От: lnkuser  
Дата: 05.07.15 08:24
Оценка:
Здравствуйте, koenjihyakkei, Вы писали:



K>Или можно в файле, где определен Log, для удобства сделать дефайн типа:


K>#define LOG *Log::instance()



Google C++ Style Guide говорит что это плохо:

Instead of using a macro to conditionally compile code ... well, don't do that at all (except, of course, for the #define guards to prevent double inclusion of header files). It makes testing much more difficult.


https://google-styleguide.googlecode.com/svn/trunk/cppguide.html#Preprocessor_Macros
Re[3]: Как наиболее корректно работать с глобальными данными
От: watchmaker  
Дата: 05.07.15 08:54
Оценка:
Здравствуйте, lnkuser, Вы писали:

L>Здравствуйте, koenjihyakkei, Вы писали:


K>>Или можно в файле, где определен Log, для удобства сделать дефайн типа:


K>>#define LOG *Log::instance()



L>Google C++ Style Guide говорит что это плохо:


L>Instead of using a macro to conditionally compile code ... well, don't do that at all (except, of course, for the #define guards to prevent double inclusion of header files). It makes testing much more difficult.


В этой цитате речь идёт совсем о другом: «conditionally compile code» — это про #if, #ifdef и прочее. В вышеприведённом макросе никаких условий нет.
Что, впрочем, не делает более привлекательной саму спорную идею заворачивания вызова в макрос.
Re[5]: Как наиболее корректно работать с глобальными данными
От: Шахтер Интернет  
Дата: 05.07.15 09:14
Оценка:
Здравствуйте, Abyx, Вы писали:

A>Здравствуйте, Шахтер, Вы писали:


Ш>>>>Это глупость. Например, хип есть глобальный объект и ты им (как правило неявно) пользуешься в большинстве программ.

A>>>во многих библиотеках делают возможность задать свои функции аллокации/деаллокации, и хип напрямую не используется.

Ш>>И что, хип после этого перестаёт быть глобальным объектом?


A>да, перестает.


Нет. Двойка.

A>в той же винде можно создать несколько разных хипов для разных потоков или модулей программы.

A>или например можно использовать arena allocator'ы.

Только они при этом будут глобальными объектами.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re: Как наиболее корректно работать с глобальными данными
От: ELazin http://rsdn.ru/forum/prj/6225353.1
Автор: ELazin
Дата: 26.10.15
Дата: 06.07.15 08:15
Оценка:
L>Допустим есть класс Log, который инициализируется при старте программы гдето в самом начале main().
L>И есть другие части программы которые работают в разных потоках, там тоже используется Log.

Что касается лога, то я обычно инициализирую экземпляр логгера на каждый файл с исходным кодом. Пишу где-нибудь в самом начале:

static Logger logger_ = Logger("log-section-name");


С log4cxx удобно. Внутри класса Logger уже какое-то глобальное состояние есть, а в моей программе как-бы и нет.
Re: Как наиболее корректно работать с глобальными данными
От: MasterZiv СССР  
Дата: 07.07.15 08:13
Оценка:
Здравствуйте, lnkuser, Вы писали:


L>Как я понял это как раз случай для синглетона:


L>
L>// other_module.cpp

L>auto g_log = Log::instance();
L>*g_log << loglevel::info << "some text";
L>




L>
L>// other_module2.cpp

L>auto g_log = Log::instance();
L>*g_log << loglevel::info << "some text2";
L>



L>Все вроде идеально, но тут будет ошибка линкования.


На таком уровне понимания проблемы тебе надо сначала почитать книжку по языку С++ или даже С,
потому что модели памяти у них одинаковые.
Почитай про классы памяти, области видимости и static.
В таком раскладе обе переменные auto g_log должны быть объявлены static, иначе будет нарушение ODR.

Да, и к работе потоков это не имеет никакого отношения (пока).
Отредактировано 07.07.2015 8:14 MasterZiv . Предыдущая версия .
Re: Как наиболее корректно работать с глобальными данными
От: MasterZiv СССР  
Дата: 07.07.15 08:27
Оценка:
Здравствуйте, lnkuser, Вы писали:

L>но опять таки, много где пишут мол старайтесь избегать extern. А что использовать взамен тогда???


Одно слово: идиоты!
Не читай говнокнижги, не читай говноинтернет, не читай советы на форумах (на этом -- можно !)
Читай книги, правильные книги, и думай, что зачем, а не живи мифами, котрые кочуют из форума в форум.


L>Как правильно спроектированные приложения решают проблему глобальных данных?


Никак, просто правильно используют глобальные данные, и всё.
Ещё раз, если это многопоточность, то глобальные данные должны защищаться от совместного доступа.


L>Какие книги есть по данной тематике (а именно физический дизайн приложения), где бы описывалось как правильно строить программу.


Нет, нет таких книг. И да, есть такие книги -- любой элементарный учебник по С, даже по С, а не по С++,
классы памяти и области видимости переменных.
Потому что ты их не знаешь, и не понимаешь, у тебя всё в кучу в голове и мешается.

Доступ к данным при многопоточности -- это другая совсем тема, по этому есть очень компактная книжка,
"Программирование на Linux, Профессиональный подход" Митчел, Оулдем, Самьюэл. Там надо главы 3, 4, 5 только прочитать.
Но учти, что multi-threaded программирование до поры было платформнозависимым, поэтому в Win всё то же самое, но
немного по-другому. Но идеи те же.
Re[2]: Как наиболее корректно работать с глобальными данными
От: VTT http://vtt.to
Дата: 07.07.15 09:12
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>Здравствуйте, lnkuser, Вы писали:


L>>но опять таки, много где пишут мол старайтесь избегать extern. А что использовать взамен тогда???


MZ>Одно слово: идиоты!

MZ>Не читай говнокнижги, не читай говноинтернет, не читай советы на форумах (на этом -- можно !)
MZ>Читай книги, правильные книги, и думай, что зачем, а не живи мифами, котрые кочуют из форума в форум.

Ну в контексте C++ в использовании extern действительно нет необходимости.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re[3]: Как наиболее корректно работать с глобальными данными
От: enji  
Дата: 07.07.15 12:16
Оценка:
Здравствуйте, VTT, Вы писали:

VTT>Ну в контексте C++ в использовании extern действительно нет необходимости.


Это как?
Re[4]: Как наиболее корректно работать с глобальными данными
От: VTT http://vtt.to
Дата: 07.07.15 19:45
Оценка:
Здравствуйте, enji, Вы писали:

E>Здравствуйте, VTT, Вы писали:


VTT>>Ну в контексте C++ в использовании extern действительно нет необходимости.


E>Это как?


Ну а для чего оно может использоваться? Глобальные переменные сподручнее делать в виде статических полей классов (да и использовать только в пределах этого класса), а трюк с шаблонами позволяет одновременно объявлять и определять эти поля в заголовочном файле.
Для внешних функций может понадобиться писать extern "C", но это уже мы попадаем в контекст C.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re[5]: Как наиболее корректно работать с глобальными данными
От: enji  
Дата: 08.07.15 08:10
Оценка:
Здравствуйте, VTT, Вы писали:

VTT>Ну а для чего оно может использоваться?

Для объявления глобальных переменных

VTT>Глобальные переменные сподручнее делать в виде статических полей классов (да и использовать только в пределах этого класса), а трюк с шаблонами позволяет одновременно объявлять и определять эти поля в заголовочном файле.


Ну с тем же успехом можно сказать, что и в for нет необходимости. Есть std::for_each, есть while

Иногда удобно использовать статический член класса, иногда — нет.
Re[7]: Как наиболее корректно работать с глобальными данными
От: enji  
Дата: 09.07.15 04:14
Оценка:
Здравствуйте, VTT, Вы писали:

E>>удобно



VTT>Для меня это последний аргумент.

Верю. но на ровном месте городить класс со статической переменной — это просто "синтаксический оверхед"

VTT>C++ это вообще не особо удобный язык: предварительное объявление всего и вся,


VTT>крякозябры,

это ты еще скалу не видел

VTT>экнмнабквх,

а?
VTT>неконсистентность,
где?

VTT>просто назвать переменную покороче i вместо полноценного item_index,

ну батенька....

VTT>И да, я скажу что C-шный трехсекционный for(;;) не нужен,

Фанат бейсика — for i = 1 to 10 step 5? Или питона — for i in xrange(1, 10, 5)?

VTT>С-style enum не нужен

Почему?

VTT>объявление переменных через запятую не нужно,

Почему?

VTT>assignment chaining не нужен,

Почему?
Отредактировано 09.07.2015 4:15 enji . Предыдущая версия .
Re[8]: Как наиболее корректно работать с глобальными данными
От: VTT http://vtt.to
Дата: 09.07.15 08:21
Оценка:
Здравствуйте, enji, Вы писали:

VTT>>Для меня это последний аргумент.

E>Верю. но на ровном месте городить класс со статической переменной — это просто "синтаксический оверхед"
Нет, это способ избежать неконтролируемого доступа к глобальным переменным.

VTT>>экнмнабквх,

E>а?
VTT>>неконсистентность,
E>где?
Да куда не плюнь... Что синтаксис, что стандартная библиотека. Чтение и слева направо, и справа налево, и даже по спирали. Потоки ввода-вывода вообще какие-то наркоманы писали, чего только стоит плеяда eback, gptr, egptr, pbase, pptr, epptr, всякие sungetc xsputn, и кидание исключений по флажкам.

VTT>>просто назвать переменную покороче i вместо полноценного item_index,

E>ну батенька....

VTT>>И да, я скажу что C-шный трехсекционный for(;;) не нужен,

E>Фанат бейсика — for i = 1 to 10 step 5? Или питона — for i in xrange(1, 10, 5)?

Скорее фанат Кобола:
PERFORM VARYING COUNTER FROM 1 BY 5 UNTIL COUNTER > 10

VTT>>С-style enum не нужен

E>Почему?
Ну в C enum это скорее замена пачки define. То, что в свежем стандарте C++ появилась возможность делать enum class, да еще и задавая базовый тип — большое достижение. Костыли типа attribute(packed) больше не нужны, и случаи неявного приведения к целочисленным типам (особенно когда они в качестве индекса используются) теперь легче отлавливать.

VTT>>объявление переменных через запятую не нужно

E>Почему?
Несвязные действия в одном statement, полный винегрет с объявлением указателей и инициализацией, ненужные сложности с отладкой.

VTT>>assignment chaining не нужен

E>Почему?
Несвязные действия в одном statement, чтение справа налево, ненужные сложности с отладкой, не имеет смысла при использовании move семантики.
Еще мне не нравится наличие у оператора присваивания возвращаемого значения (и требование возвращать ссылку для соответствия copy assignable), из-за этого случается нехилое количество факапов вида if(i = 42).
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re[2]: Как наиболее корректно работать с глобальными данными
От: Философ Ад http://vk.com/id10256428
Дата: 09.07.15 19:24
Оценка:
Здравствуйте, Don Reba, Вы писали:

DR>Здравствуйте, lnkuser, Вы писали:


L>>Как я понял это как раз случай для синглетона:


DR>Синглетоны — это те же глобальные объекты и часто считаются антипаттерном.


Почти правильно, но нужно уточнять: используйте фабрики. Если сейчас нужен синглтон, обойтись можно и фабрикой, которая будет возвращать один и тот же объект, но завтра от синглтона будет тяжело избавиться, а от фабрики будет избавляться не нужно.
Всё сказанное выше — личное мнение, если не указано обратное.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.