Policy based approach with logger [closed] - c++

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
I spend some time with redesigning a logger class I did once into a policy based approach after reading an article about policy based design and wanting to try something myself.
Some code:
template <class Filter, class Formatter, class Outputter>
class LoggerImpl : public LoggerBase {
public:
LoggerImpl(const Filter& filter = Filter(), const Formatter& formatter = Formatter(), const Outputter& outputter = Outputter());
~LoggerImpl();
void log(int channel, int loglevel, const char* msg, va_list list) const;
private:
const Filter mFilter;
const Formatter mFormatter;
const Outputter mOutputter;
};
template <class Filter, class Formatter, class Outputter>
LoggerImpl<Filter, Formatter, Outputter>::LoggerImpl(const Filter& filter, const Formatter& formatter, const Outputter& outputter) :
mFilter(filter), mFormatter(formatter), mOutputter(outputter) {
debuglib::logdispatch::LoggerMgr.addLogger(this);
}
typedef LoggerImpl<NoFilter, SimpleFormatter, ConsoleOutputter> ConsoleLogger;
typedef LoggerImpl<ChannelFilter, SimpleFormatter, VSOutputter> SimpleChannelVSLogger;
typedef LoggerImpl<NoFilter, SimpleFormatter, FileOutputter> FileLogger;
ConsoleLogger c;
SimpleChannelVSLogger a(const ChannelFilter(1));
FileLogger f(NoFilter(), SimpleFormatter(), FileOutputter("log.txt"));
// macro for sending log message to all current created loggers
LOG(1, WARN, "Test %d", 1423);
Depending on the logger I need to pass additional information like the logchannel within the SimpleChannelVsLogger or the filename of the logfile in the FileOututter.
I'm passing the parameters to the constructor of LoggerImpl as const reference and copy them into the object stored in the logger class afterwards.
There is a need to copy them because the lifetime extension is not transitive through a function argument that occurs when binding the temporary created object to a const reference (more on this here: Does a const reference prolong the life of a temporary?).
So first thing here: If I don't want to use pointers as I am not interested in runtime allocation when using templates, I guess there is no other solution than copying the temporary created objects in the way like above?
The actual problem in the copying stuff now comes with the FileOutputter:
An ofstream cannot be copied of course, so how can I copy the FileOutputter object containing the stream?
I came up with following solution to overcome this problem:
struct FileOutputter {
// c_tor
FileOutputter() {}
// c_tor
explicit FileOutputter(const char* fname) {
mStream = std::make_shared<std::fstream>(fname, std::fstream::out);
}
// The copy c_tor will be invoked while creating any logger with FileOutputter
// FileLogger f(NoFilter(), SimpleFormatter(), FileOutputter("log.txt"));
// as all incoming paramters from the constructors stack frame are copied into the current instance of the logger
// as copying a file-stream is not permitted and not good under any means
// a shared pointer is used in the copy c_tor
// to keep the original stream until no reference is referring to it anymore
FileOutputter(const FileOutputter& other) {
mStream = other.mStream;
}
~FileOutputter() {
}
void out(const char* msg) const {
*mStream << msg;
}
std::shared_ptr<std::fstream> mStream;
};
Somehow I have to feeling that this seems a bit complex for a "simple logger class", however this may be just a "problem" with the policy based design approach in this case.
Any thoughts are welcome

It is correct that you should copy the objects if you are going to store them as members in your class.
Storing references is dangerous as it is possible to pass temporary objects as parameters to your ctor, which will lead to dangling references when the temporaries are destructed.
Passing the parameters as pointers is an alternative, but this aproach is also problematic as it then becomes possible to pass in a nullptr (NULL-value), and you have to check for this.
Another alternative would be to move the values i.e. pass the parameters as r-value references. This will avoid copying, however it will require the client to either pass temporaries or std::move objects when calling the ctor. It would no longer be possible to pass l-value references.
// Define ctor as taking r-value references.
template <class Filter, class Formatter, class Outputter>
LoggerImpl<Filter, Formatter, Outputter>::LoggerImpl(Filter&& filter, Formatter&& formatter, Outputter&& outputter) :
mFilter(std::move(filter)), mFormatter(std::move(formatter)), mOutputter(std::move(outputter)) {
// ...
}
/* ... */
// Calling ctor.
FileLogger f1(NoFilter(), SimpleFormatter(), FileOutputter("log.txt")); // OK, temporaries.
FileOutputter fout("log.txt");
FileLogger f2(NoFilter(), SimpleFormatter(), fout); // Illegal, fout is l-value.
FileLogger f3(NoFilter(), SimpleFormatter(), std::move(fout)); // OK, passing r-value. fout may not be used after this!
If you decide to go with the copy-approach then I recommend to instead pass your parameters by value in the ctor. This will allow the compiler to perform optimizations as copy elision (read: Want Speed? Pass by Value).
template <class Filter, class Formatter, class Outputter>
LoggerImpl<Filter, Formatter, Outputter>::LoggerImpl(Filter filter, Formatter formatter, Outputter outputter) :
mFilter(std::move(filter)), mFormatter(std::move(formatter)), mOutputter(std::move(outputter)) {
// ...
}
Using the above definition: in the best case scenario the compiler will elide the copy and the members will be move constructed (when passing a temporary object).
In the worst case scenario: a copy and a move construction will be performed (when passing an l-value).
Using your version (passing parameters as reference to const), a copy will always be performed as the compiler can not perform optimizations.
For move construction to work, you will have to make sure that the types that are passed as parameters is move constructible (either implicitly or using a declared move ctor). If a type is not move constructible it will be copy constructed.
When it comes to copying the stream in FileOutputter, using std::shared_ptr seems like a good solution, although you should initialize mStream in the initialization list instead of assigning in the ctor body:
explicit FileOutputter(const char* fname)
: mStream(std::make_shared<std::ofstream>(fname)) {}
// Note: Use std::ofstream for writing (it has the out-flag enabled by default).
// There is another flag that may be of interest: std::ios::app that forces
// all output to be appended at the end of the file. Without this, the file
// will be cleared of all contents when it is opened.
A std::ofstream is non-copyable and passing around a smart pointer (make sure to use std::shared_ptr though) is probably the simplest solution in your case and also in my opinion, contrary to what you say, not overy complex.
Another approach would be to make the stream member static, but then every instance of FileOutputter would use the same std::ofstream object and it would not be possible to use parallel logger objects writing to different files etc.
Alternatively you could move the stream as std::ofstream is non-copyable but movable. This would however require you to make FileOutputter movable and non-copyable (and probably LoggerImpl as well), as using a "moved" object, other than its dtor, can result in UB. Making an object that manages move-only types to itself become move-only may make a lot of sense sometimes though.
std::ofstream out{"log.txt"};
std::ofstream out2{std::move(out)} // OK, std::ofstream is moveable.
out2 << "Writing to stream"; // Ok.
out << "Writing to stream"; // Illegal, out has had its guts ripped out.
Also, in the example provided, you don't need to declare a copy ctor or a dtor for FileOutputter, as they will be implicitly generated by the compiler.

You can have the policy classes contain static functions so ideally you would want FileOutputter to look like:
template<std::string fileName>
struct FileOutputter {
static void out(const char* msg) const {
std::ofstream out(fileName);
out << msg;
}
};
You would create an instance of LoggerImpl like this
LoggerImpl<NoFilter, SimpleFormatter, FileOutputter<"log.txt"> > FileLogger;
This would mean your LoggerImpl doesn't need to store a copy of the policy classes it just need to call their static functions.
Unfortunately this won't work because you can't have strings as template arguments but you can build a table of strings and pass the index of the filename in your string table. So again you would ideally want this to look like:
//This class should be a singleton
class StringManager
{
std::vector<std::string> m_string;
public:
void addString(const std::string &str);
const std::string &getString(int index);
int getIndexOf(const std::string &str);
};
Then your FileLogger would get an int as a template parameter and it would be the index of the string in the StringManager. This also wouldn't quite work because you need the index available at compile time and StringManager would be initialized at run time. So you would have to build the string table manually and manually write in the index of your string. so your code would look like (after you make StringManager a singleton:
StringManager::instance()->addString("a");
StringManager::instance()->addString("b");
StringManager::instance()->addString("c");
StringManager::instance()->addString("d");
StringManager::instance()->addString("log.txt");
LoggerImpl<NoFilter, SimpleFormatter, FileOutputter<4> > FileLogger;
You would have to make sure that StringManager is fully initialized before the first instance of FileLogger is created.
It's a little ugly but using templates with strings is a little ugly.
You could also do something like:
template <class FilenameHolder>
struct FileOutputter {
static void out(const char* msg) const {
std::ofstream out(FilenameHolder::fileName());
out << msg;
}
};
class MyFileNameHolder
{
static std::string fileName() {return "log.txt";}
};

Related

Is there any way to correctly std::move const data?

My problem is that I have to choose between copying const data, and moving non-const data, and I conceptually do not see a reason why, in my situation, it wouldn't be safe to move const data to a const destination. I am also aware that moving from a const object is illogical - so perhaps there's some other way to achieve what I am attempting to do...
I have a Builder class that sets up a BigData object that I don't want to have to copy around. Once the builder is finished it 'emits' the data as const, because it shouldn't be modified after that point. The builder is temporary, and can then be discarded, but the data should live on, as a const member in a wrapper class that can interrogate it. The builder is a generic type (doesn't know about the details of the data), while the wrapper is specific to the contents of the data. This is achieved via type-erasure within the data, but that's not relevant to this question, except to make it clear that it isn't desirable to merge the builder and wrapper.
The below code describes the above, and does not compile unless you remove BigData's move constructor. The compile error shows that this code would copy the data, which is undesirable:
prog.cpp:26:57: error: use of deleted function ‘constexpr BigData::BigData(const BigData&)’
Wrapper( const BigData&& In ) : Data( std::move(In) ) {}
^
class BigData
{
// Only a builder can create data
friend class Builder;
BigData() = default;
public:
// Move only
BigData( BigData&& ) = default;
};
class Builder
{
BigData Data;
public:
const BigData&& Build() & { return std::move(Data); }
// Functions to set up data go here
};
class Wrapper
{
const BigData Data;
public:
Wrapper( const BigData&& In ) : Data( std::move(In) ) {}
// Functions to query data go here
};
std::unique_ptr<Wrapper> DoBuild()
{
Builder b;
// call functions on b to set up data here
return std::make_unique<Wrapper>( b.Build() );
}
The above code can also be found at https://ideone.com/tFkVEd.
The problem can be fixed by changing all const BigData&& into just BigData&& but I don't want the data to be changeable after it is retrieved from Build().
Finally, the problem could also be solved by returning the data from Build in a unique_ptr<const BigData>, and have the Wrapper take ownership of this, but my problem with this is that a pointer can be null, so all usage of Data within Wrapper would have to first check that the Data pointer is valid. This is why I want to move the const data into the wrapper via an rvalue reference, and also why the wrapper must be constructed with the data.
Is there any way to achieve what I want, or have I come up against a limitation of c++?
Many thanks in advance for your input.
NB: Related questions that I have encountered while trying to solve this myself:
Why can we use `std::move` on a `const` object?
Return value or rvalue reference?
Is returning by rvalue reference more efficient?
There is no way to std::move a const T as it would mutate the given object. (Unless you make the members mutable and make a T(const T&&) constructor to do the move, which I would not recommend).
A better solution in my opinion would be to make all the mutating functions private, such that only the builder can access them, and return a T&&.
firstly, to move an object, exactly means to modify it.
secondly, I don't think you should prevent all the modifications to BigData. trying to return a BigData const&& and prevent it to be modified, is just like trying to prevent it to be const_cast-ed, which is a wasted effort. in this case, you can just declare that to modify a BigData inside a Builder is definitely an undefined behavior, except to move it and then return a BigData&&.
thirdly, if you want it not to be modified exactly (except to be moved), you should consider if to construct a Wrapper is the only usage for Builder. if so, why not provide a constructer Wrapper(Build&) to do that? it can prevent all potential modifications from Builder to Wrapper.
finally, to answer the question how to move a const BigData object, I suggest BigData(BigData const&&) and const_cast, which means you allow all const BigData to be moved as if it was non-const.

Notify the instance of the object when it is copied

I have a class with lazy initialization. For the sake of simplicity let's say that it is a database proxy. An object of the class can be in any of two states: Ready and Initialized. Initially, when you construct the object, you specify the database address, and the object becomes Ready, but doesn't connect to the DB yet. When you call any function which wants to access the DB the object performs the connection and changes its state to Initialized. I want the object to initialize when I do anything with it.
Including, I want to make this object copyable. However, I don't want to allow the uninitialized object to be copied: in this case two connections will be performed later. Instead I would like to notify the object when I'm trying to copy it so it can perform initialization, switch the state and only then copy to another object.
Here is a sample code with the behavior I'm trying to achieve.
class Foo {
public:
Foo(Config conf) :
initialized(false)
{
// process the config somehow
}
void tryInitialize() {
if (!initialized) {
// perform the connection
initialized = true;
connection = createConnection();
}
}
void accessDatabase() {
tryInitialize();
// do something
}
Foo(const Foo& other) {
other.tryInitialize(); // this is a problem because other is const
initialized = true;
connection = other.connection;
}
private:
bool initialized;
DBConnection connection;
};
The problem still arises if I declare copy ctor as Foo(Foo other) because in this case the original object wouldn't be modified as well. Of course, this is achievable with move ctor, but I'd like to use copy ctor as well.
P.S. The real problem is not much about databases but complex enough to be explained here. So you can assume that no real world problems about databases matter here: connection attempts always succeed, having two instances of the same connection is OK, etc.
You can mark your private members as mutable and make you tryInitialize() method const (or you can write some other private initialize method with constness).
This solution is a bit dirty but anyway it solves your problem :)
If i'm understanding this correctly, you want a copy constructor that can modify the passed in argument. If this is true then what your are saying is correct, foo( foo other ) would not work because that would only pass a copy of the argument, and foo( const foo& other ) would not be modifiable in the copy constructor. The solution is simply passing by reference.
Foo(Foo& other) {
initialized = true;
other.tryInitialize(); // Now will be able to modify other
connection = other.connection;
}
Reference article below for const correctness of copy constructors below, and I quote, " Pass by non-const reference ONLY if the function will modify the parameter and it is the intent to change the caller's copy of the data, otherwise pass by const reference."
http://www.cplusplus.com/articles/y8hv0pDG/
Reference article for mutable below, "mutable - applies to non-static class members of non-reference non-const type and specifies that the member does not affect the externally visible state of the class (as often used for mutexes, memo caches, lazy evaluation, and access instrumentation). mutable members of const classes are modifiable. (Note: the C++ language grammar treats mutable as a storage-class-specifier, but it does not affect storage class.)"
http://en.cppreference.com/w/cpp/language/cv
See example of using mutable from "Effective C++ Third Edition" by Scott Meyers, and apply for alternative solution to pass by reference.
class CTextBlock {
public:
...
std::size_t length() const;
private:
char *pText;
mutable std::size_t textLength;
mutable bool lengthIsValid;
};
std::size_t CTextBlock::length() const
{
if(!lengthIsValid) {
textLength = std::strlen(pText);
lengthIsValid = true;
}
return textLength;
}

Should a std::string class member be a pointer?

And why/why not?
Say I have a class which takes a string in the constructor and stores it. Should this class member be a pointer, or just a value?
class X {
X(const std::string& s): s(s) {}
const std::string s;
};
Or...
class X {
X(const std::string* s): s(s) {}
const std::string* s;
};
If I was storing a primitive type, I'd take a copy. If I was storing an object, I'd use a pointer.
I feel like I want to copy that string, but I don't know when to decide that. Should I copy vectors? Sets? Maps? Entire JSON files...?
EDIT:
Sounds like I need to read up on move semantics. But regardless, I'd like to make my question a little more specific:
If I have a 10 megabyte file as a const string, I really don't want to copy that.
If I'm newing up 100 objects, passing a 5 character const string into each one's constructor, none of them ought to have ownership. Probably just take a copy of the string.
So (assuming I'm not completely wrong) it's obvious what to do from outside the class, but when you're designing class GenericTextHaver, how do you decide the method of text-having?
If all you need is a class that takes a const string in its constructor, and allows you to get a const string with the same value out of it, how do you decide how to represent it internally?
Should a std::string class member be a pointer?
No
And why not?
Because std::string, like every other object in the standard library, and every other well-written object in c++ is designed to be treated as a value.
It may or may not use pointers internally - that is not your concern. All you need to know is that it's beautifully written and behaves extremely efficiently (actually more efficient than you can probably imagine right now) when treated like a value... particularly if you use move-construction.
I feel like I want to copy that string, but I don't know when to decide that. Should I copy vectors? Sets? Maps? Entire JSON files...?
Yes. A well-written class has "value semantics" (this means it's designed to be treated like a value) - therefore copied and moved.
Once upon a time, when I was first writing code, pointers were often the most efficient way to get a computer to do something quickly. These days, with memory caches, pipelines and prefetching, copying is almost always faster. (yes, really!)
In a multi-processor environment, copying is very much faster in all but the most extreme cases.
If I have a 10 megabyte file as a const string, I really don't want to copy that.
If you need a copy of it, then copy it. If you really just mean to move it, then std::move it.
If I'm newing up 100 objects, passing a 5 character const string into each one's constructor, none of them ought to have ownership. Probably just take a copy of the string.
A 5-character string is so cheap to copy that you should not even think about it. Just copy it. Believe it or not, std::string is written with the full knowledge that most strings are short, and they're often copied. There won't even be any memory allocation involved.
So (assuming I'm not completely wrong) it's obvious what to do from outside the class, but when you're designing class GenericTextHaver, how do you decide the method of text-having?
Express the code in the most elegant way you can that succinctly conveys your intent. Let the compiler make decisions about how the machine code will look - that it's job. Hundreds of thousands of people have given their time to ensure that it does that job better than you ever will.
If all you need is a class that takes a const string in its constructor, and allows you to get a const string with the same value out of it, how do you decide how to represent it internally?
In almost all cases, store a copy. If 2 instances actually need to share the same string then consider something else, like a std::shared_ptr. But in that case, they probably would not only need to share a string so the 'shared state' should be encapsulated in some other object (ideally with value semantics!)
OK, stop talking - show me how the class should look
class X {
public:
// either like this - take a copy and move into place
X(std::string s) : s(std::move(s)) {}
// or like this - which gives a *miniscule* performance improvement in a
// few corner cases
/*
X(const std::string& s) : s(s) {} // from a const ref
X(std::string&& s) : s(std::move(s)) {} // from an r-value reference
*/
// ok - you made _s const, so this whole class is now not assignable
const std::string s;
// another way is to have a private member and a const accessor
// you will then be able to assign an X to another X if you wish
/*
const std::string& value() const {
return s;
}
private:
std::string s;
*/
};
If the constructor truly "takes a string and stores it", then of course your class needs to contain a std::string data member. A pointer would only point at some other string that you don't actually own, let alone "store":
struct X
{
explicit X(std::string s) : s_(std::move(s)) {}
std::string s_;
};
Note that since we're taking ownership of the string, we may as well take it by value and then move from the constructor argument.
In most cases you will want to be copying by value. If the std::string gets destroyed outside of X, X will not know about it and result in undesired behavior. However, if we want to do this without taking any copies, a natural thing to do might be to use std::unique_ptr<std::string> and use the std::move operator on it:
class X {
public:
std::unique_ptr<std::string> m_str;
X(std::unique_ptr<std::string> str)
: m_str(std::move(str)) { }
}
By doing this, note that the original std::unique_ptr will be empty. The ownership of the data has been transferred. The nice thing about this is that it protects the data without needing the overhead of a copy.
Alternately, if you still want it accessible from the outside world, you can use an std::shared_ptr<std::string> instead, though in this case care must be taken.
Yes, generally speaking, it is fine to have a class that holds a pointer to an object but you will need to implement a more complex behaviour in order to make your class safe. First, as one of the previous responders noticed it is dangerous to keep a pointer to the outside string as it can be destroyed without the knowledge of the class X. This means that the initializing string must be copied or moved when an instance of X is constructed. Secondly, since the member X.s now points to the string object allocated on the heap (with operator new), the class X needs a destructor to do the proper clean-up:
class X {
public:
X(const string& val) {
cout << "copied " << val << endl;
s = new string(val);
}
X(string&& val) {
cout << "moved " << val << endl;
s = new string(std::move(val));
}
~X() {
delete s;
}
private:
const string *s;
};
int main() {
string s = "hello world";
X x1(s); // copy
X x2("hello world"); // move
return 0;
}
Note, that theoretically you can have a constructor that takes a const string* as well. It will require more checks (nullptr), will support only copy semantics, and it may look as follows:
X(const string* val) : s(nullptr) {
if(val != nullptr)
s = new string(*val);
}
These are techniques. When you design your class the specifics of the problem at hand will dictate whether to have a value or a pointer member.

Pass by value or const reference? [duplicate]

This question already has answers here:
Are the days of passing const std::string & as a parameter over?
(13 answers)
Closed 9 years ago.
There is a set of good rules to determine whether pass by value or const reference
If the function intends to change the argument as a side effect, take
it by non-const reference.
If the function doesn't modify its argument and the argument is of
primitive type, take it by value.
Otherwise take it by const reference, except in the following
cases: If the function would then need to make a copy of the const
reference anyway, take it by value.
For constructor as following, how to determine it?
class A
{
public:
A(string str) : mStr(str) {} // here which is better,
// pass by value or const reference?
void setString(string str) { mStr = str; } // how about here?
private:
string mStr;
};
In this particular case, and assuming C++11 and move construction/assignment for strings, you should take the argument by value and move it to the member for the constructor.
A::A(string str) : mStr(std::move(str)) {}
The case of the setter is a bit trickier and I am not sure whether you really want/need to optimize every bit of it... If you want to optimize the most you could provide two overloads, one taking an rvalue reference and another taking a const lvalue reference. At any rate, the const lvalue reference is probably a good enough approach:
void A::setString(string const& str) { mStr = str; }
Why the difference?
In the case of the constructor, the member is not yet built, so it is going to need to allocate memory. You can move that memory allocation (and actual copying of the data, but that is the leaser cost) to the interface, so that if the caller has a temporary it can be forwarded without an additional memory allocation.
In the case of assignment the things are a bit more complicated. If the current size of the string is large enough to hold the new value, then no allocation is required, but if the string is not large enough, then it will need to reallocate. If the allocation is moved to the interface (by-value argument), it will be executed always even when it is unnecessary. If the allocation is done inside the function (const reference argument) then for a small set of cases (those where the argument is a temporary that is larger then the current buffer) an allocation that could otherwise have been avoided would be done.
The article you site is not a good reference for software
engineering. (It is also likely out of date, given that it
talks about move semantics and is dated from 2003.)
The general rule is simple: pass class types by const reference,
and other types by value. There are explicit exceptions: in
keeping with the conventions of the standard library, it is also
usual to pass iterators and functional objects by value.
Anything else is optimization, and shouldn't be undertaken until
the profiler says you have to.
In this case it is better to pass argument by const reference. Reason: string is a class type, you don't modify it and it can be arbitrary big.
It is always better to use the member initialization list to initialize your values as it provides with the following advantages:
1) The assignment version, creates a default constructor to initialize mStr and then assigned a new value on top of the default-constructed one. Using the MIL avoids this wasted construction because the arguments in the list are used as constructor arguments.
2) It's the only place to initialize constant variables unless these are just intergers which you can use enums in the class. enum T{v=2};
3) It's the place to initialize references.
This is what I would suggest:
#include <iostream>
#include <string>
class A
{
private:
std::string mStr;
public:
A(std::string str):mStr(str){}
//if you are using an older version of C++11, then use
//std::string &str instead
inline const void setString(std::string str)
{
mStr = str;
}
const std::string getString() const
{
return mStr;
}
};
int main()
{
A a("this is a 1st test.");
a.setString("this is a 2nd test.");
std::cout<<a.getString()<<std::endl;
}
Have a look at this:
http://www.cplusplus.com/forum/articles/17820/

efficiently passing and returning objects in c++

I have a Visual Studio 2008 C++ application where a function Foo::CreateBar uses an internal function to populate a buffer with some data as below. I understand that VS2008 will use return value optimization (RVO) to ensure the Foo::GetData() call will not incur a copy, but will I incur copies for the Bar constructor? How about for returning the Bar object from Foo::CreateBar Is there a more efficient way to do this? Do I need to redefine my Buffer as boost::shared_ptr< std::vector< BYTE > >?
typedef std::vector< BYTE > Buffer;
class Bar
{
public:
explicit Bar( Buffer buffer ) : buffer_( buffer ) { };
// ...
private:
Buffer buffer_;
};
class Foo
{
public:
Bar CreateBar() const { return Bar( GetData() ); };
// ...
private:
static Buffer GetData()
{
Buffer buffer;
// populate the buffer...
return buffer;
};
};
Thanks,
PaulH
You can answer this question for sure by examining the assembler code that's generated for this source - modify the compiler output options to produce a listing interlaced with the source code.
shared_ptr is not appropriate, in any case. That's a device for the programmer to manage designs requiring shared objects efficiently, not to fool the compiler into not constructing more objects than it should.
Using a const reference as parameter of the Bar constructor
explicit Bar( const Buffer & buffer ) : buffer_( buffer ) { };
will surely avoid you the copy caused by the pass-by-value in the constructor call, but you will have a copy in the construction of the buffer_ field.
shared_ptr would work. unique_ptr would work, because ownership is transferred.
You could also use out parameters to avoid the copy. Add a factory function...
class Bar
{
public:
explicit Bar( void (*factory)(Buffer& buffer) ) { factory(buffer_); }
// ...
private:
Buffer buffer_;
};
class Foo
{
public:
Bar CreateBar() const { return Bar( GetData ); }
// ...
private:
static void GetData(Buffer& buffer)
{
// populate the buffer...
}
};
You should make a couple of different tests and see what your compiler does with it. In the code above there are as many as 4 copies of the buffer:
Inside GetData
Return value of GetData
Argument of constructor
Member
The compiler by doing RVO/NRVO can merge 1 and 2 into a single object. Moreover it can merge those two objects with 3 by placing the three objects in the same memory location. What it cannot do is place that object and 4 in the same memory address (in the general case).
On that particular copy, you might elide it by default initializing the internal member vector (usually no cost) and swapping the contents of the argument with the member:
explicit Bar( Buffer buffer ) // pass by value if your compiler optimizes it
: buffer_() // default construct: no cost
{
buffer_.swap( buffer ); // swap: no cost
};
Also note that this is the whole reason for rvalue references in the upcoming standard, in C++0x (I know that is not available in VS08) the compiler will use move semantics to avoid the copies while maintaining the code readable (readable as in closest to the intentions than to tricks to avoid the cost).
You will definitely incur a copy for the buffer.
Why do you want to use a shared pointer? Is it a shared data? If so - then go for it. If not - than you have to copy. We don't really know what is your design, do we?
If you're sensitive to copy operations, you may want to force copy operations to be explicit or even prevent them altogether. If you make the copy constructor and assignment operator protected/private (or use boost::noncopyable) the compiler will produce an error whenever it tries to generate code that copies the object. Of course if you do want to make copies sometimes you'll need to provide a method for explicitly doing so, but that ensures that you never accidentally introduce a copy operation.