Можно ли делать C++ RAII с неиспользуемым/временным объектом?

bleyman

Типа, кот: http://ideone.com/Oav40
Хочется наколенную хрень чтобы например писать дико простой XML. Например. В наколенной хрени хочется автоматически закрывать тэги. Чтобы например пишешь xml.section("asd" оно фигачит открывающий тэг, и автоматически фигачит закрывающий тэг в конце scope.
Проблема в том, что по ходу если ты создаёшь безымянный временный объект, то он убивается сразу, а не в конце scope. Вот как в примере по ссылке, XmlSection section(xml, "asd"); работает как я хочу, XmlSection(xml, "asd"); убивается сразу.
Я перед этим написал ещё более стрёмный код где реально xml.section("asd") возвращало специальный экземпляр приватного класса (пришлось в результате использовать слово mutable! но он точно так же убивался сразу.
Типа, так нельзя делать ваще, да?
А может как-нибудь по-другому можно лучше сделать? Так довольно странно получается что структура кода не повторяет структуру эксемельки, код плоский, а та — нестится, может как-нибудь сделать стрёмный макрос наподобие BOOST_FOREACH, чтобы писать

XML_SECTION("tag")
{
// blah blah
}

elenangel

я, например, делал такую хрень для логирования. это не совсем то, что ты хотел, но имхо что-то похожее в этом есть.

#include <iostream>
#include <sstream>

class Log
{
public:
template <typename T>
Log& operator , (const T &t)
{
s << t;
return *this;
}

~Log
{
std::cout << s.str << std::endl;
}

private:
std::stringstream s;
};



int main(int, char *[])
{
int value = 7;
Log "test: ", value;

return 0;
}

P.S. в конструкторе такого лога можно добавлять в начало результирующей строки дату, время, номер треда, etc.

Serpent555

я, например, делал такую хрень для логирования
А зачем использовать стрёмную запятую вместо вполне уместного здесь оператора "<<"?

elenangel

ну на мой вкус запятая выглядит естественнее и набирается проще, чем дурацкие "клювики"

PITACHOK

может ты вот это ищешь?
http://www.boost.org/doc/libs/1_51_0/libs/scope_exit/doc/htm...

Maurog

XmlSection(xml, "asd"); убивается сразу.
брюки превращаются.... в элегантные шорты : http://ideone.com/5I6a3t :grin:

Maurog

А может как-нибудь по-другому можно лучше сделать?
посмотри на всякие xml билдеры
первое, что нагуглилось: http://code.google.com/p/java-xmlbuilder/
легко ложится на C++

apl13

struct SimpleXMLWriter
{
private:
Еще раз, в какие шорты?

Maurog

вариация на тему http://ideone.com/ix6Lkk :grin:

apl13

Хотел, было, процитировать бивера, но решил поизвращаться:
class SimpleXMLSection;

struct SimpleXMLWriter {
SimpleXMLWriter;
SimpleXMLSection &begin_section(const string & tag);
SimpleXMLWriter &end_section;
SimpleXMLWriter &write_value(const string & tag, const string & value);
protected:
SimpleXMLWriter(SimpleXMLWriter const &);
};

struct SimpleXMLSection: public SimpleXMLWriter {
SimpleXMLSection(SimpleXMLWriter &w, string const &tag): SimpleXMLWriter(w) {
tags.push(tag);
}
~SimpleXMLSection {
end_section;
}
};

SimpleXMLSection SimpleXMLWriter::begin_section(const string &tag) {
return SimpleXMLSection(*this, tag);
}

int main {
SimpleXMLWriter xml.
section(xml, "lulz").
write_value("asd", "dsa").
section(xml, "lulz2").
write_value("asdd", "ddsa");
}

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

Maurog

вот так даже поприятнее http://ideone.com/5ulyeT :grin:

apl13

O, ja, meine liebe, komm zu mir!
class SimpleXMLSection;

struct SimpleXMLWriter {
SimpleXMLWriter;
SimpleXMLSection &begin_section(const string & tag);
virtual SimpleXMLWriter &end_section;
SimpleXMLWriter &write_value(const string & tag, const string & value);
protected:
SimpleXMLWriter(SimpleXMLWriter const &);
};

class SimpleXMLSection: public SimpleXMLWriter {
SimpleXMLWriter &parent;
SimpleXMLWriter &root;
bool alive;
public:
SimpleXMLSection(SimpleXMLWriter &w, string const &tag): SimpleXMLWriter(w parent(w root(w alive(true) {
root.tags.push(tag);
}
SimpleXMLSection(SimpleXMLSection &w, string const &tag): SimpleXMLWriter(w parent(w root(w.root alive(true) {
root.tags.push(tag);
}
SimpleXMLWriter &end_section {
if(alive) {
alive = false;
root.end_section;
}
else {
parent.end_section;
}
}
~SimpleXMLSection {
if(alive) root.end_section;
}
};

SimpleXMLSection SimpleXMLWriter::begin_section(const string &tag) {
return SimpleXMLSection(*this, tag);
}

int main {
// 's patch hasn't made it into the main tree yet, so this still won't compile.
SimpleXMLWriter xml.
section(xml, "lulz").
write_value("asd", "dsa").
section(xml, "lulz2").
write_value("asdd", "ddsa").
end_section.
write_value("hello", "pinkie-pie");
}

А эта, думаю, еще больше не соберется. :D

bleyman

Я, кстати, в результате так сделал:

class XmlSectionHelper
{
    SimpleXMLWriter & parent;
    bool state;
public:
    XmlSectionHelper(SimpleXMLWriter & a_parent, const string & tag) : parent(a_parent state(false)
    {
     parent.begin_section(tag);
    }

    ~XmlSectionHelper
    {
     parent.end_section;
    }

    bool loop_test
    {
     state = !state;
     return state; // returns true first time, false second time.
    }
};

#define XML_SECTION(xml, ...) for (XmlSectionHelper __xml_section_helper(xml, __VA_ARGS__); __xml_section_helper.loop_test;)

int main
{
    SimpleXMLWriter xml;
    
    XML_SECTION(xml, "lulz1")
    {
     xml.write_value("key1", "value1");
     XML_SECTION(xml, "lulz2")
     {
     xml.write_value("key2", "value2");
     }
     xml.write_value("key3", "value3");
    }
}

ava3443

__xml_section_helper
современные компиляторы с намёками static analysis будут ругаться на то что одну и ту же переменную заново определяешь - т.е. MSVC10 будет 100%, но мне кажется что также и свежий gcc, не говоря уж про clang.
некоторые компиляторы (типа древнего MSVC6, но не только!) вообще не скомпилируют т.к. в них scope переменной объявленной в цикле for не ограничен телом цикла.
можно попробовать генерить имя переменной как-то так:
#define CONCATENATE_DIRECT(s1, s2) s1##s2
#define CONCATENATE(s1, s2) CONCATENATE_DIRECT(s1, s2)
#define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __LINE__)
#define XML_SECTION(xml, ...) for (XmlSectionHelper ANONYMOUS_VARIABLE(__xml_section_helperxml, __VA_ARGS__); ANONYMOUS_VARIABLE(__xml_section_helper).loop_test;)

rosali

Не знаю насколько это стандартно, но в gcc (4.7.1) у меня работает просто через if, вот так
 
#define XML_SECTION(xml, ...) if (XmlSectionHelper __xml_section_helper = XmlSectionHelper(xml, __VA_ARGS__

только operator bool надо задекларировать.
При этом такое не компилируется
 
#define XML_SECTION(xml, ...) if (XmlSectionHelper __xml_section_helper(xml, __VA_ARGS__

и такое тоже
 
#define XML_SECTION(xml, ...) if (XmlSectionHelper __xml_section_helper = XmlSectionHelper(xml, __VA_ARGS__ *dummy = (XmlSectionHelper*)1)

Странный язык...

apl13

современные компиляторы с намёками static analysis будут ругаться на то что одну и ту же переменную заново определяешь - т.е. MSVC10 будет 100%, но мне кажется что также и свежий gcc, не говоря уж про clang.
MSVC-то понятно, но компиляторы C++-то, вроде, имеют представление об области видимости.

ava3443

MSVC-то понятно, но компиляторы C++-то, вроде, имеют представление об области видимости.
в MSVC >= 1310 (2003 т.е.) с этим в порядке.
однако автор activemq-cpp (с которой мне приходится иметь дело принципиально шлющий нафиг с древними MSVC, тем не менее данной проблемой озаботился и написал макрос чуть по-другому, из чего я делаю вывод что не только у MSVC такие баги бывают.
/**
* The synchronized macro defines a mechanism for synchronizing
* a section of code. The macro must be passed an object that
* implements the Synchronizable interface.
*
* The macro works by creating a for loop that will loop exactly
* once, creating a Lock object that is scoped to the loop. Once
* the loop completes and exits the Lock object goes out of scope
* releasing the lock on object W. For added safety the if else
* is used because not all compiles restrict the lifetime of
* loop variables to the loop, they will however restrict them
* to the scope of the else.
*
* The macro would be used as follows.
*
* Synchronizable X;
*
* somefunction
* {
* synchronized(X)
* {
* // Do something that needs synchronizing.
* }
* }
*/

#define synchronized(W) \
if(false){} \
else \
for( decaf::util::concurrent::Lock lock_W(W); \
lock_W.isLocked; lock_W.unlock )

bleyman

современные компиляторы с намёками static analysis будут ругаться на то что одну и ту же переменную заново определяешь - т.е. MSVC10 будет 100%, но мне кажется что также и свежий gcc, не говоря уж про clang.

Я проверял на ideone.com, у них весьма дико свежий гцц, и не ругается.
Прозреваю что потому, что BOOST_FOREACH заимплементен похожим образом, и у тебя должна быть возможность сказать

BOOST_FOREACH(xtype & x, xs) BOOST_FOREACH(ytype & y, ys)
{
...
}

Они наверное могли бы использовать __LINE__ но у них тогда бы всё наебнулось бы точно так же если бы компиляторы выдавали варнинги на эту тему. Boost > compilers, поэтому те молчат в тряпочку.
Про MSVC 6.0 я знаю, но могу положить МПХ к счастью, из древних компиляторов меня только GCC 2.9 (кажется) и 3.4 заботят, к счастью. И то, первый — редко. Хотя показанный Vond'ом подход я запомнил, вдруг пригодится.
Объявление переменной внутри if дичайше нестандартно же, не? Влом читать стандарт. И не нужно, ибо всё равно надобно определить оператор тайпкаста, я лучше явно функцию определю, мне в моём подходе нравится практически полное отсутствие чорной магеи (я тут недавно портировал код который писал два года назад, блядь, как я того мудака ненавижу!)

erotic

современные компиляторы с намёками static analysis будут ругаться на то что одну и ту же переменную заново определяешь
Это же разные переменные. Можно ругаться на перекрытие имен, но это зависит от флагов компиляции.

ava3443

включённый static analyzer в MSVC10 с флагами по-умолчанию ругается

Maurog

современные компиляторы с намёками static analysis будут ругаться на то что одну и ту же переменную заново определяешь - т.е. MSVC10 будет 100%, но мне кажется что также и свежий gcc, не говоря уж про clang.
вот такой код компилируется без предупреждений\ошибок в VS2010 + /W4 (или /Wall)

int f = 0;
{
int f = 0;
if (int f = 9)
{
f = 10;
}
f = 1;
}
f = 8;

Maurog

Объявление переменной внутри if дичайше нестандартно же, не?
вполне себе стандартно

ava3443

неверное утверждение, которое легко проверяется.
я уже два раза написал - "static analyzer"
вот /W4 почему-то никогда не включал, а static analyzer - всегда когда архитектура позволяет (в msvc10 он только с x86)
выглядит вот так:
1>...\pgexecrules.cpp(103): warning C6246: Local declaration of 'it' hides declaration of the same name in outer scope. For additional information, see previous declaration at line '88' of '...\pgexecrules.cpp': Lines: 88

Maurog

я уже два раза написал - "static analyzer"
никогда не юзал эту шнягу =\
оказывается, и не поюзаю, ибо
Code Analysis can be found in the Premium and Ultimate editions of Visual Studio 2010.

ava3443

Мда, не знал про то что в Pro нет. У меня из MSDN выкачан дистрибутив Ultimate, других и не видел.
Однако пишут что и с Professional тоже можно как-то использовать, например:
http://www.iprogrammable.com/2011/06/15/visual-studio-2010-p...
FxCop is a standalone Code Analysis version that comes together with “Microsoft Windows SDK for Windows 7 and .NET Framework 4 Version 7.1”

Maurog

если не ошибаюсь, FxCop не работает с нативным кодом, только с .net
http://stackoverflow.com/questions/2233578/fxcop-or-equivale...

ava3443

Да, точно. Для C++ это PREFast...
Кстати, в vc11 (aka Visual Studio 2012) его включили во всех варианты студии:
 - с полным набором правил начиная с Pro и дороже;
 - выборочно с ключевыми правилами даже в Express.
http://blogs.msdn.com/b/codeanalysis/archive/2012/03/09/what...
Во, кстати John Carmack рекламирует микрософтовский /analyze: http://www.altdevblogaday.com/2011/12/24/static-code-analysi...

Maurog

спасибо, подрочичитал :grin:
Оставить комментарий
Имя или ник:
Комментарий: