Re: RAII not always applicable?
Hi!
I read between the lines and I think I know what you mean. You want to
initialize objects directly to reduce state within the object. That is
you don't have to check for initialization conditions when using the
object. A small silly example:
// invariant: name.size() > 5
struct InitMe {
InitMe() : initialized(false) {} //does not fully init
void init(std::string const& newName)
{
if(initialized)
throw std::runtime_error("...");
if(newName.size()<=5)
throw std::runtime_error("...");
name = newName;
initialized = true;
}
void printName() //needs initialized==true
{
if( ! initialized) //I don't want to check this
throw std::runtime_error("...");
std::cout.write(name.c_str(), 5);
}
private:
std::string name;
};
InitMe me;
std::string newName;
stream >> newName;
me.init(newName);
me.printName();
restor schrieb:
First situation is the std::istream interface for reading the data;
that is, the operator >>. If you want to read a piece of data from the
stream interface, you first have to create an object with a useless
value and then override that value with operator >>, like in the below
example:
T val;
std::cin >> val;
To me the obvious way to do this is (or "work around"):
// invariant: name.size() > 5
struct InitMe2 {
InitMe(std::string const& newName)
: name(newName)
{
if(newName.size()<=5)
throw std::runtime_error("...");
}
void printName()
{
std::cout.write(name.c_str(), 5);
}
private:
const std::string name; //can make const
};
std::string newName;
stream >> newName;
InitMe2 me(newName);
This approach also gives opportunity to make the class members const
(like InitMe2::name) or even to make the class instance "me" const.
The general approach would either pass all needed variables to the ctor
or use a dedicated struct with all necessary data. This struct could
also define an operator >> to read itself from a stream. The struct
could also be placed inside the class:
struct InitMe3 {
struct Data {
std::string name;
};
InitMe3(Data const& newData)
: data(newData)
{
if(data.name.size()<=5)
throw std::runtime_error("...");
}
void printName()
{
std::cout.write(data.name.c_str(), 5);
}
private:
const Data data;
};
Second situation is when two objects need to be constructed and they
need to refer to one another. For illustration, the below is an
example of symmetric coroutines from boost::coroutine:
[snip]
Here because producer coroutine needs a reference to consumer
coroutine and vice versa they have to be initialized in two stages.
I cannot suggest anything much useful. Setting up references can be done
via pointers to raw storage.
//ignore alignment for a while:
char buf[sizeof(Producer)];
Consumer consumer(buf); //only receives raw buffer
new(buf) Producer(consumer);
The problem here is not to allow the consumer access to the producer
until the producer is constructed. This will require some call to the
consumer to notify it of this:
consumer.notifyProducerIsConstructed();
This is again a two stage init. I think it cannot be avoided.
Frank
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]