I wonder if the following syntax can be "admitted" or if good practices consider this as coming from hell. The goal would be to add a level of protection to force the developer to be well-conscious of what he is doing. Here is the syntax :
class MyClass
{
public:
template<bool RemoveProtection = false>
inline std::ofstream& writeStream()
{
static_assert(RemoveProtection, "You're doing it wrong");
return _writeStream;
}
inline const std::ofstream& writeStream() const
{
return _writeStream;
}
protected:
std::ofstream _writeStream;
};
The use would be :
x.writeStream().good(); // <- OK
x.writeStream().put('c'); // <- NOT OK
x.writeStream<true>().put('c'); // <- OK
I find this as a convenient way to tell to the developer : "Beware, you are using a low-level function and you have to be careful with what you are doing". Is it a "acceptable" way of providing a direct access to class members with a kind of "protection" ? Is there other way of coding a such thing ?
Have a look at meagar's comment:
You're making your code ugly, harder to maintain and inconvenient for... what exactly? Define your interface. That is the interface to your class. Do not allow developers to bypass it by using some ridiculous template flag hackery. If you're writing code, you always have to know what you're doing. Having to explicitly type <true> to indicate you especially know what you're doing is just... very, very wrong-headed. Developers have documentation. They don't need training wheels and artificial restrictions, they need clear concise code that lets them get things done. – meagar 2012-10-06 02:41:53Z
A class you provide to others should never be able to get into an unpredicted state when another user uses it. An unpredicted state is in this case a state which you never considered when you were writing the class. As such you should either never allow access to low-level methods of your class or document possible flaws.
Lets say you're writing a logger:
struct MyLogger{
MyLogger(std::string filename) : stream(filename.c_str()){}
template <typename T>
MyLogger& operator<<(const T& v){ stream << v << " "; return *this;}
private:
std::ofstream stream;
};
Ignore that there isn't a copy constructor and that the assignment operand is missing. Also ignore that it's a crude logger, it doesn't even provide a timestamp. However, as you can see, the state of the logger depends completely on the logger's methods, e.g. if the file has been successfully opened it won't be closed until the logger gets destroyed.
Now say that we use your approach:
struct MyLogger{
MyLogger(std::string filename) : stream(filename.c_str()){}
template <typename T>
MyLogger& operator<<(const T& v){ stream << v << " "; return *this;}
template<bool RemoveProtection = false>
inline std::ofstream& writeStream()
{
static_assert(RemoveProtection, "You're doing it wrong");
return stream;
}
inline const std::ofstream& writeStream() const
{
return stream;
}
private:
std::ofstream stream;
};
And now someone uses the following code
logger.writeStream<true>.close();
Bang. Your logger is broken. Of course it's the users fault, since they used <true>, didn't they? But more often than not a user is going to copy example code, especially if he uses a library for the first time. The user sees your example
logger.writeStream().good(); // <- OK
logger.writeStream().put('c'); // <- NOT OK
logger.writeStream<true>().put('c'); // <- OK
and ignores the documentation completely at first. Then he's going to use the first and the last version. Later he discovers that the last version works every time! What a miraculous thing <true> is. And then he starts to blame you for evil things that happen, so you have protect yourself from blatant flames with a documentation which includes a warning:
/**
* \brief Returns a reference to the internal write stream
*
* \note You have to use the template parameter `<true>` in order to use it
*
* \warning Please note that this will return a reference to the internal
* write stream. As such you shouldn't create any state in which
* the logger cannot work, such as closing the stream.
*/
template<bool RemoveProtection = false>
inline std::ofstream& writeStream()
{
static_assert(RemoveProtection, "You're doing it wrong");
return stream;
}
So, what did we get? We still had to put that warning somewhere. It would have been much simpler if we made stream public:
struct MyLogger{
MyLogger(std::string filename) : stream(filename.c_str()){}
template <typename T>
MyLogger& operator<<(const T& v){ stream << v << " "; return *this;}
/**
* The internal write stream. Please look out that you don't create
* any state in which the logger cannot work, such as closing the stream.
*/
std::ofstream stream;
};
or sticked to
/** >put warning here< */
inline std::ofstream & writeStream()
{
return stream;
}
Woops. So either don't allow access to your low-level methods (build a wrapper to specific std::ofstream methods if they should be allowed to use them) or document the possible flaws which can happen if you alter the object's internals to much, but don't go the middle-way and make it look okay with a static_assert.
Related
Background
I have auto generated concrete message types from a XML -> C++ generator.
GenMsg1, GenMsg2, ... , GenMsgN
All of these generated classes are from an XML schema. Technically I can edit their cpp and hpp files but I would prefer to not touch these as much as possible. They all have guaranteed functions that I would like to be able to call generically.
NOTE: I cannot get away from the above situation as this is a design limitation from another project. Also, I just used raw pointers in this simple example. I understand this is not best practice, its just for showing a general idea.
Goal
I am looking to process the above generated messages generically on my side.
Idea 1 and 2
My first idea was to just create and general "Message" class that was templated to hold one of the above types with a simple enum for identifying what type of message it is. The problem with this is I cannot just pass around a pointer to Message because it needs the template type parameter so this is obviously a no-go.
My next thought was to use the Curiously Recurring Template Pattern but that has the same issues as above.
Idea 3
After a lot of reading on messaging frameworks my next thought was that std::variant might be an option.
I have the following example which works but it uses double pointers and templated functions to access. If the wrong datatype is used this will throw an exception at runtime (which makes it quite clear this is the issue) but I could see this being annoying down the line as far as tracking the source of the throw.
I keep trying to read up on the std::visit but it does not make a whole lot of sense to me. I do not really want to implement a separate visitor class with a bunch of functions by hand when all of the functions in the generated classes are autogenerated already(like foo in the example below) and are ready to be called when the type is known. Additionally, they are guaranteed to exist. So it would be kind of nice to be able to call a foo() in Message and have it dive into the internal Representation and call its foo.
I have a MsgType enum in there that I could use as well. When the internal representation is set, I could set that and use it for deducing type... But this seems like its just duplicating effort already done by the std::variant so I scrapped its use but kept it in the code blow in case someone here had a new idea where something like that could be useful.
Any ideas on design moving forward? This seems like the most promising route, but I am open to ideas. Also, with my reality of having to conform to other peoples design decisions I realize that this code will "smell" a bit no matter what. I am just trying to make it as clean as possible on my end.
Idea 3 Code
#include <iostream>
#include <variant>
enum class MsgType { NOTYPE = 0, GenMessage1 = 1, GenMessage2 = 2, GenMessage3 = 3 };
class GenMessage1
{
public:
void foo() {std::cout << "Msg 1" << std::endl;}
};
class GenMessage2
{
public:
void foo() { std::cout << "Msg 2" << std::endl; }
};
class GenMessage3
{
public:
void foo() { std::cout << "Msg 3" << std::endl; }
};
class Message
{
private:
MsgType msgType;
std::string xmlStrRep;
std::variant<GenMessage1*, GenMessage2*, GenMessage3*> internalRep;
public:
Message()
{
this->msgType = MsgType::NOTYPE;
this->xmlStrRep = "";
}
template <typename T>
void setInternalRep(T* internalRep)
{
this->internalRep = internalRep;
}
template <typename T>
void getInternalRep(T retrieved)
{
*retrieved = getInternalRepHelper(*retrieved);
}
template <typename T>
T getInternalRepHelper(T retrieved)
{
return std::get<T>(this->internalRep);
}
void foo()
{
//call into interal representation and call its foo
}
};
int main()
{
Message* msg = new Message();
GenMessage3* incomingMsg = new GenMessage3();
GenMessage3* retrievedMsg;
msg->setInternalRep(incomingMsg);
msg->getInternalRep(&retrievedMsg);
retrievedMsg->foo();
return 0;
}
Outputs:
Msg 3
I think std::visit is, as you suspected, what you need. You can implement your foo() function like this:
void foo()
{
std::visit([](auto* message) {message->foo();}, this->internalRep);
}
Using a generic lambda (taking auto), it can be thought of as a template function, where the lambda's argument message is the actual type of the message in the variant, and you can use it directly. Provided all the messages have the same interface that you want to use, then you can do this with all the interface functions.
I am author of a library with a number of optimization algorithms, in which I already invested quite a bit of profiling/tuning. I am currently writing front-end programs for this library.
At the moment, the library routines themselves are pretty much black boxes. Consider a method along the lines of bool fit(vector_t const& data, double targetError). They work and do what I want them to do, but for the frontends, a bit of runtime information would be nice. For example, it would be nice if information like "current error" or "number of iterations left" could be displayed. I do not want a simple if (verbose) cerr << "Info\n"; pattern, since the library should be equally usable in a GUI environment.
I deliberately write could, because I want to keep the impact of this information emission as low as possible. How can I define and implement an interface, that
Emits an object with run-time information whenever an observer is registered
has minimal run-time costs with regards to the algorithm if an information object is emitted, and
has close to no run-time costs if no observer is registered?
Basically, the run-time costs of this optional introspection should be as low as possible, and close to zero if no introspection is wanted. How can this be achieved? Do libraries exist for this? (I guess the answer is yes, and probably hidden in the Boost project, but without known what to look for…)
Just move all costs to compile time!
The easiest approach is:
template<bool logging>
bool fit(vector_t const& data, double targetError)
{
// ... computations ...
if (logging) {
std::cout << "This is log entry\n";
}
// ... computations ...
}
Usage:
fit<true>(data, epsilon); // Will print logs.
fit<false>(data, epsilon); // Will not print logs with zero overhead.
Nowadays almost any compiler will optimize all such checks out while compiling.
A more flexible approach is to pass a logger as a template parameter:
class ConsoleLogger
{
public:
template<typename... Args>
static void log(Args... args) {
// Print args using compile-time recursion.
// Anyway, this prototype is only an example.
// Your may define any logging interface you wish.
}
};
class FileLogger
{
// Some implementation of log() ...
};
class RemoteCloudLogger
{
// Some implementation of log() ...
};
class NullLogger
{
template<typename... Args>
static void log(Args... args) {
// Intentionally left blank. Any calls will be optimized out.
}
};
template<typename Logger>
bool fit(vector_t const& data, double targetError)
{
// ... computations ...
Logger::log("Current error: ", currentError);
Logger::log("Iterations passed: ", i);
// ... computations ...
}
Usage:
fit<ConsoleLogger>(data, epsilon); // Will log to console.
fit<RemoteCloudLogger>(data, epsilon); // Will log to a file on remote cloud server.
fit<NullLogger>(data, epsilon); // Any calls to Logger::log will be optimized out, yielding zero overhead.
Obviously, you can write a logger class which will collect all logged information to a structure with introspection data.
For example, you might define fit's interface as follows:
template<typename Logger>
bool fit(vector_t const& data, double targetError, IntrospectionData& data)
{...}
and define only two logging classes: IntrospectionDataLogger and NullLogger, whose log methods accept a reference to IntrospectionData structure. And again, the last class contains empty methods that will be thrown away by your compiler.
You could allocate the struct on the stack and pass a reference to the observer. The observer can then do whatever is necessary with the struct, e.g. copy it, print it, display it in a GUI etc. Alternatively, if you want to track only one or two properties you might want to just pass them as separate arguments.
class Observer
{
...
public:
virtual void observe(MyInfoStruct *info) = 0;
}
...
if(hasObservers()) // ideally inline
{
MyInfoStruct info = {currentError, bla, bla};
for(observer : observers)
{
observer->observe(&info);
}
}
That way there should be no overhead when there are no observers except for the if statement. The overhead for emitting the information should be dominated by the virtual calls to the observers.
Further optimizations:
You may want to outline the presumably cold observer iteration code into a separate function to improve code locality for the hot path.
If there are any frequent virtual calls in your code anyway, consider adding an "Observed" version of the interface and try to perform the observation work there, and completely omit it from the original version. If the observed version can easily be injected once upon placement of the observer, you may be able to omit checking for observers redundantly. If you just want to track arguments to a function this is easy, by making the "Observed" version just forward to the original, however if you want to track information while an algorith is running this may not be practical.
Depending on what you want to track, it may be possible to write this as a template:
struct NoObserverDispatch {
bool hasObservers() {return false;}
void observe(MyInfoStruct *) {}
};
struct GenericObserverDispatch {
bool hasObservers() {return !observers.isEmpty();}
void observe(MyInfoStruct *) { for (obs : observers) obs->observe(info); }
private:
vector<unique_ptr<Observer> > observers;
};
template<typename ObserverDispatch>
class Fitter
{
ObserverDispatch obs;
virtual bool fit(vector_t const& data, double targetError)
{
...
if(obs.hasObservers()) { // will be optimized away unless needed
MyInfoStruct info = {currentError, bla, bla};
obs.observe(&info);
}
}
};
Obviously this assumes that you can replace the Fitter whenever an observer is placed. Another option would be to let the user pick the template instantiation, but that may be less clean and has the drawback that you will have to ship the code.
I'd like to create a C++ ostream object that only outputs anything if a condition holds true, to use for debugging. What would be the easiest way to do this? I know that boost has classes that make this easy, but I'd like to know if there's a simple way to do it without boost. The documentation makes it seem like subclassing ostream::sentry would make this possible, but I can't find any source saying that's something that you can/should do.
Don't subclass, it's easier to use a wrapper:
class MaybeOstream
{
public:
MaybeOstream(std::ostream& stream_) : stream(stream_), bOutput(true) {}
void enable(bool bEnable) { bOutput = bEnable; }
template<typename T>
MaybeOstream& operator<< (T x)
{
if(bOutput)
stream << x;
return *this;
}
// Add other wrappers around ostream: operator void*, good(), fail(),
// eof(), etc., which just call through to the ostream
private:
std::ostream& stream;
bool bOutput;
}
Take a look at this paper on filtered streambufs.
Unless I am thoroughly mistaken, the getter/setter pattern is a common pattern used for two things:
To make a private variable so that it can be used, but never modified, by only providing a getVariable method (or, more rarely, only modifiable, by only providing a setVariable method).
To make sure that, in the future, if you happen to have a problem to which a good solution would be simply to treat the variable before it goes in and/or out of the class, you can treat the variable by using an actual implementation on the getter and setter methods instead of simply returning or setting the values. That way, the change doesn't propagate to the rest of the code.
Question #1: Am I missing any use of accessors or are any of my assumptions incorrect? I'm not sure if I am correct on those.
Question #2: Are there any sort of template goodness that can keep me from having to write the accessors for my member variables? I didn't find any.
Question #3: Would the following class template be a good way of implementing a getter without having to actually write the accesor?
template <class T>
struct TemplateParameterIndirection // This hack works for MinGW's GCC 4.4.1, dunno others
{
typedef T Type;
};
template <typename T,class Owner>
class Getter
{
public:
friend class TemplateParameterIndirection<Owner>::Type; // Befriends template parameter
template <typename ... Args>
Getter(Args args) : value(args ...) {} // Uses C++0x
T get() { return value; }
protected:
T value;
};
class Window
{
public:
Getter<uint32_t,Window> width;
Getter<uint32_t,Window> height;
void resize(uint32_t width,uint32_t height)
{
// do actual window resizing logic
width.value = width; // access permitted: Getter befriends Window
height.value = height; // same here
}
};
void someExternalFunction()
{
Window win;
win.resize(640,480); // Ok: public method
// This works: Getter::get() is public
std::cout << "Current window size: " << win.width.get() << 'x' << win.height.get() << ".\n";
// This doesn't work: Getter::value is private
win.width.value = 640;
win.height.value = 480;
}
It looks fair to me, and I could even reimplement the get logic by using some other partial template specialization trickery. The same can be applied to some sort of Setter or even GetterSetter class templates.
What are your thoughts?
Whilst the solution is neat from implementation point of view, architectually, it's only halfway there. The point of the Getter/Setter pattern is to give the clas control over it's data and to decrease coupling (i.e. other class knowing how data is stored). This solution achieves the former but not quite the latter.
In fact the other class now has to know two things - the name of the variable and the method on the getter (i.e. .get()) instead of one - e.g. getWidth(). This causes increased coupling.
Having said all that, this is splitting proverbial architectural hairs. It doesn't matter all that much at the end of the day.
EDIT OK, now for shits and giggles, here is a version of the getter using operators, so you don't have to do .value or .get()
template <class T>
struct TemplateParameterIndirection // This hack works for MinGW's GCC 4.4.1, dunno others
{
typedef T Type;
};
template <typename T,class Owner>
class Getter
{
public:
friend TemplateParameterIndirection<Owner>::Type; // Befriends template parameter
operator T()
{
return value;
}
protected:
T value;
T& operator=( T other )
{
value = other;
return value;
}
};
class Window
{
public:
Getter<int,Window> _width;
Getter<int,Window> _height;
void resize(int width,int height)
{
// do actual window resizing logic
_width = width; //using the operator
_height = height; //using the operator
}
};
void someExternalFunction()
{
Window win;
win.resize(640,480); // Ok: public method
int w2 = win._width; //using the operator
//win._height = 480; //KABOOM
}
EDIT Fixed hardcoded assignment operator. This should work reasonably well if the type itself has an assignment operator. By default structs have those so for simple ones it should work out of the box.
For more complex classes you will need to implement an assignment operator which is fair enough. With RVO and Copy On Write optimizations, this should be reasonably efficient at run time.
FWIW here are my opinions on your questions:
Typically the point is that there is business logic or other constraints enforced in the setter. You can also have calculated or virtual variables by decoupling the instance variable with accessor methods.
Not that I know of. Projects I've worked on have had a family of C macros to stamp out such methods
Yes; I think that's pretty neat. I just worry it's not worth the trouble, it'll just confuse other developers (one more concept they need to fit in their head) and isn't saving much over stamping out such methods manually.
Since Igor Zevaka posted one version of this, I'll post one I wrote a long time ago. This is slightly different -- I observed at the time that most real use of get/set pairs (that actually did anything) was to enforce the value of a variable staying within a pre-determined range. This is a bit more extensive, such as adding I/O operators, where extractor still enforces the defined range. It also has a bit of test/exercise code to show the general idea of what it does and how it does it:
#include <exception>
#include <iostream>
#include <functional>
template <class T, class less=std::less<T> >
class bounded {
const T lower_, upper_;
T val_;
bool check(T const &value) {
return less()(value, lower_) || less()(upper_, value);
}
void assign(T const &value) {
if (check(value))
throw std::domain_error("Out of Range");
val_ = value;
}
public:
bounded(T const &lower, T const &upper)
: lower_(lower), upper_(upper) {}
bounded(bounded const &init)
: lower_(init.lower), upper_(init.upper)
{
assign(init);
}
bounded &operator=(T const &v) { assign(v); return *this; }
operator T() const { return val_; }
friend std::istream &operator>>(std::istream &is, bounded &b) {
T temp;
is >> temp;
if (b.check(temp))
is.setstate(std::ios::failbit);
else
b.val_ = temp;
return is;
}
};
#ifdef TEST
#include <iostream>
#include <sstream>
int main() {
bounded<int> x(0, 512);
try {
x = 21;
std::cout << x << std::endl;
x = 1024;
std::cout << x << std::endl;
}
catch(std::domain_error &e) {
std::cerr << "Exception: " << e.what() << std::endl;
}
std::stringstream input("1 2048");
while (input>>x)
std::cout << x << std::endl;
return 0;
}
#endif
You can also use a getter or setter type method to get or set computable values, much the way properties are used in other languages like C#
I can't think of reasonable way to abstract the getting and setting of an unknown number of values / properties.
I'm not familiar enough with the C++ox standard to comment.
This may be overkill in this case but you should check out the attorney/client idiom for judicious friendship usage. Before finding this idiom, I avoided friendship altogether.
http://www.ddj.com/cpp/184402053/
And now the question, and what if you need a setter as well.
I don't know about you, but I tend to have (roughly) two types of classes:
class for the logic
blobs
The blobs are just loose collections of all the properties of a Business Object. For example a Person will have a surname, firstname, several addresses, several professions... so Person may not have logic.
For the blobs, I tend to use the canonical private attribute + getter + setter, since it abstracts the actual implementation from the client.
However, although your template (and its evolution by Igor Zeveka) are really nice, they do not address the setting problem and they do not address binary compatibility issues.
I guess I would probably resort to macros...
Something like:
// Interface
// Not how DEFINE does not repeat the type ;)
#define DECLARE_VALUE(Object, Type, Name, Seq) **Black Magic Here**
#define DEFINE_VALUE(Object, Name, Seq) ** Black Magic Here**
// Obvious macros
#define DECLARE_VALUER_GETTER(Type, Name, Seq)\
public: boost::call_traits<Type>::const_reference Name() const
#define DEFINE_VALUE_GETTER(Object, Name)\
boost::call_traits<Name##_type>::const_reference Object::Name ()const\
{ return m_##Name; }
#define DECLARE_VALUE_SETTER(Object, Type, Name)\
public: Type& Name();\
public: Object& Name(boost::call_traits<Type>::param_type i);
#define DEFINE_VALUE_SETTER(Object, Name)\
Name##_type& Object::Name() { return m_##Name; }\
Object& Object::Name(boost::call_traits<Name##_type>::param_type i)\
{ m_##Name = i; return *this; }
Which would be used like:
// window.h
DECLARE_VALUE(Window, int, width, (GETTER)(SETTER));
// window.cpp
DEFINE_VALUE(Window, width, (GETTER)); // setter needs a bit of logic
Window& Window::width(int i) // Always seems a waste not to return anything!
{
if (i < 0) throw std::logic_error();
m_width = i;
return *this;
} // Window::width
With a bit of preprocessor magic it would work quite well!
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/tuple/rem.hpp>
#define DECLARE_VALUE_ITER(r, data, elem)\
DECLARE_VALUE_##elem ( BOOST_PP_TUPLE_REM(3)(data) )
#define DEFINE_VALUE_ITER(r, data, elem)\
DEFINE_VALUE_##elem ( BOOST_PP_TUPLE_REM(2)(data) )
#define DECLARE_VALUE(Object, Type, Name, Seq)\
public: typedef Type Name##_type;\
private: Type m_##Name;\
BOOST_PP_SEQ_FOREACH(DECLARE_VALUE_ITER, (Object, Type, Name), Seq)
#define DEFINE_VALUE(Object, Name, Seq)\
BOOST_PP_SEQ_FOREACH(DEFINE_VALUE_ITER, (Object, Name), Seq)
Okay, not type safe, and all, but:
it's a reasonable set of macro I think
it's easy to use, the user only ever have to worry about 2 macros after all, though like templates the errors could get hairy
use of boost.call_traits for efficiency (const& / value choice)
there is more functionality there: getter/setter duo
it is, unfortunately, a set of macros... and will not complain if you ever
it wreaks havoc on the accessors (public, protected, private) so it's best not to intersped it throughout the class
Here is the canonical example then:
class Window
{
// Best get done with it
DECLARE_VALUE(Window, int, width, (GETTER));
DECLARE_VALUE(Window, int, height, (GETTER));
// don't know which is the current access level, so better define it
public:
};
You're solving the wrong problem. In a well-designed application, getters and setters should be rare, not automated. A meaningful class provides some kind of abstraction. It is not simply a collection of members, it models a concept that is more than just the sum of its member variables. And it typically doesn't even make sense to expose individual members.
A class should expose the operations that make sense on the concept that it models. Most member variables are there to maintain this abstraction, to store the state that you need. But it typically shouldn't be accessed directly. That is why it is a private member of the class in the first place.
Rather than finding easier ways to write car.getFrontLeftWheel(), ask yourself why the user of the class would ever need the front left wheel in the first place. Do you usually manipulate that wheel directly when driving? The car is supposed to take care of all the wheel-spinning business for you, isn't it?
This is where I think #defines are still useful.
The template version is complicated and hard to understand - the define version is obvious
#define Getter(t, n)\
t n;\
t get_##n() { return n; }
class Window
{
Getter(int, height);
}
I am sure I have the syntax slightly wrong - but you get the point.
If there was a well known set of templates in, say, boost then I would use them. But I would not write my own.
I'm facing problems with the design of a C++ library of mine. It is a library for reading streams that support a feature I haven't found on other "stream" implementations. It is not really important why I've decided to start writing it. The point is I have a stream class that provides two important behaviours through multiple inheritance: shareability and seekability.
Shareable streams are those that have a shareBlock(size_t length) method that returns a new stream that shares resources with its parent stream (e.g. using the same memory block used by parent stream). Seekable streams are those that are.. well, seekable. Through a method seek(), these classes can seek to a given point in the stream. Not all streams of the library are shareable and/or seekable.
A stream class that both provides implementation for seeking and sharing resources inherits interface classes called Seekable and Shareable. That's all good if I know the type of such a stream, but, sometimes, I might want a function to accept as argument a stream that simply fulfills the quality of being seekable and shareable at the same time, regardless of which stream class it actually is. I could do that creating yet another class that inherits both Seekable and Shareable and taking a reference to that type, but then I would have to make my classes that are both seekable and shareable inherit from that class. If more "behavioural classes" like those were to be added, I would need to make several modifications everywhere in the code, soon leading to unmaintainable code. Is there a way to solve this dilemma? If not, then I'm absolutely coming to understand why people are not satisfied by multiple inheritance. It almost does the job, but, just then, it doesn't :D
Any help is appreciated.
-- 2nd edit, preferred problem resolution --
At first I thought Managu's solution would be my preferred one. However, Matthieu M. came with another I preferred over Managu's: to use boost::enable_if<>. I would like to use Managu's solution if BOOST_MPL_ASSERT produced messages weren't so creepy. If there was any way to create instructive compile-time error messages, I would surely do that way. But, as I said, the methods available produce creepy messages. So I prefer the (much) lesser instructive, yet cleaner message produced when boost::enable_if<> conditions are not met.
I've created some macros to ease the task to write template functions that take arguments inheriting select class types, here they go:
// SonettoEnableIfDerivedMacros.h
#ifndef SONETTO_ENABLEIFDERIVEDMACROS_H
#define SONETTO_ENABLEIFDERIVEDMACROS_H
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/array/elem.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/and.hpp>
#include <boost/type_traits/is_base_and_derived.hpp>
#include <boost/utility/enable_if.hpp>
/*
For each (TemplateArgument,DerivedClassType) preprocessor tuple,
expand: `boost::is_base_and_derived<DerivedClassType,TemplateArgument>,'
*/
#define SONETTO_ENABLE_IF_DERIVED_EXPAND_CONDITION(z,n,data) \
boost::is_base_and_derived<BOOST_PP_TUPLE_ELEM(2,1,BOOST_PP_ARRAY_ELEM(n,data)), \
BOOST_PP_TUPLE_ELEM(2,0,BOOST_PP_ARRAY_ELEM(n,data))>,
/*
ReturnType: Return type of the function
DerivationsArray: Boost.Preprocessor array containing tuples in the form
(TemplateArgument,DerivedClassType) (see
SONETTO_ENABLE_IF_DERIVED_EXPAND_CONDITION)
Expands:
typename boost::enable_if<
boost::mpl::and_<
boost::is_base_and_derived<DerivedClassType,TemplateArgument>,
...
boost::mpl::bool_<true> // Used to nullify trailing comma
>, ReturnType>::type
*/
#define SONETTO_ENABLE_IF_DERIVED(ReturnType,DerivationsArray) \
typename boost::enable_if< \
boost::mpl::and_< \
BOOST_PP_REPEAT(BOOST_PP_ARRAY_SIZE(DerivationsArray), \
SONETTO_ENABLE_IF_DERIVED_EXPAND_CONDITION,DerivationsArray) \
boost::mpl::bool_<true> \
>, ReturnType>::type
#endif
// main.cpp: Usage example
#include <iostream>
#include "SonettoEnableIfDerivedMacros.h"
class BehaviourA
{
public:
void behaveLikeA() const { std::cout << "behaveLikeA()\n"; }
};
class BehaviourB
{
public:
void behaveLikeB() const { std::cout << "behaveLikeB()\n"; }
};
class BehaviourC
{
public:
void behaveLikeC() const { std::cout << "behaveLikeC()\n"; }
};
class CompoundBehaviourAB : public BehaviourA, public BehaviourB {};
class CompoundBehaviourAC : public BehaviourA, public BehaviourC {};
class SingleBehaviourA : public BehaviourA {};
template <class MustBeAB>
SONETTO_ENABLE_IF_DERIVED(void,(2,((MustBeAB,BehaviourA),(MustBeAB,BehaviourB))))
myFunction(MustBeAB &ab)
{
ab.behaveLikeA();
ab.behaveLikeB();
}
int main()
{
CompoundBehaviourAB ab;
CompoundBehaviourAC ac;
SingleBehaviourA a;
myFunction(ab); // Ok, prints `behaveLikeA()' and `behaveLikeB()'
myFunction(ac); // Fails with `error: no matching function for
// call to `myFunction(CompoundBehaviourAC&)''
myFunction(a); // Fails with `error: no matching function for
// call to `myFunction(SingleBehaviourA&)''
}
As you can see, the error messages are exceptionally clean (at least in GCC 3.4.5). But they can be misleading. It doesn't inform you that you've passed the wrong argument type. It informs you that the function doesn't exist (and, in fact, it doesn't due to SFINAE; but that may not be exactly clear to the user). Still, I prefer those clean messages over those randomStuff ... ************** garbage ************** BOOST_MPL_ASSERT produces.
If you find any bugs in this code, please edit and correct them, or post a comment in that regard. The one major issue I find in those macros is that they're limited to some Boost.Preprocessor limits. Here, for example, I can only pass a DerivationsArray of up to 4 items to SONETTO_ENABLE_IF_DERIVED(). I think those limits are configurable though, and maybe they will even be lifted in upcoming C++1x standard, won't they? Please, correct me if I'm wrong. I don't remember if they have suggested changes to the preprocessor.
Thank you.
Just a few thoughts:
STL has this same sort of problem with iterators and functors. The solution there was basically to remove types from the equation all together, document the requirements (as "concepts"), and use what amounts to duck typing. This fits well a policy of compile-time polymorphism.
Perhaps a midground would be to create a template function which statically checks its conditions at instantiation. Here's a sketch (which I don't guarantee will compile).
class shareable {...};
class seekable {...};
template <typename StreamType>
void needs_sharable_and_seekable(const StreamType& stream)
{
BOOST_STATIC_ASSERT(boost::is_base_and_derived<shareable, StreamType>::value);
BOOST_STATIC_ASSERT(boost::is_base_and_derived<seekable, StreamType>::value);
....
}
Edit: Spent a few minutes making sure things compiled, and "cleaning up" the error messages:
#include <boost/type_traits/is_base_and_derived.hpp>
#include <boost/mpl/assert.hpp>
class shareable {};
class seekable {};
class both : public shareable, public seekable
{
};
template <typename StreamType>
void dosomething(const StreamType& dummy)
{
BOOST_MPL_ASSERT_MSG((boost::is_base_and_derived<shareable, StreamType>::value),
dosomething_requires_shareable_stream,
(StreamType));
BOOST_MPL_ASSERT_MSG((boost::is_base_and_derived<seekable, StreamType>::value),
dosomething_requires_seekable_stream,
(StreamType));
}
int main()
{
both b;
shareable s1;
seekable s2;
dosomething(b);
dosomething(s1);
dosomething(s2);
}
Take a look at boost::enable_if
// Before
template <class Stream>
some_type some_function(const Stream& c);
// After
template <class Stream>
boost::enable_if<
boost::mpl::and_<
boost::is_base_and_derived<Shareable,Stream>,
boost::is_base_and_derived<Seekable,Stream>
>,
some_type
>
some_function(const Stream& c);
Thanks to SFINAE this function will only be considered if Stream satisfies the requirement, ie here derive from both Shareable and Seekable.
How about using a template method?
template <typename STREAM>
void doSomething(STREAM &stream)
{
stream.share();
stream.seek(...);
}
You might want the Decorator pattern.
Assuming both Seekable and Shareable have common ancestor, one way I can think of is trying to downcast (of course, asserts replaced with your error-checking):
void foo(Stream *s) {
assert(s != NULL);
assert(dynamic_cast<Seekable*>(s) != NULL);
assert(dynamic_cast<Shareable*>(s) != NULL);
}
Replace 'shareable' and 'seekable' with 'in' and 'out' and find your 'io' solution. In a library similar problems should have similar solutions.