I also have to point out that C++ and C are two different languages with different patterns and idioms. C++ has better alternatives for many C constructs which are more type-safe and thus preferable. IN your case, I would throw an exception in this case. If you ban catch(...)
in your code, it will terminate your program. When the exception is propagated, the compiler will also call destructors of objects and thus do clean-up. If you haven't, I recommend you read up on resource-acquisition-is-initialization (RAII). Since it looks like you are transitioning from C to C++, I recommend to read the tour of C++ which shows fundamental C++ principles. For RAII, the gist is to manage resources in special handler objects which allocate in the constructor and deallocate in the destructor, and implement move semantics. This way, you cannot leak resources. Example implementations are std::vector
, std::unique_ptr
or std::iostream
. As another example, consider mutex locking/unlocking:
class Mutex {
public:
void lock() { ... }
void unlock() { ... }
};
When you use it, it easy to forget unlocking in your code, especially when making modifications to existing code. Also, in case of exceptions, you need try/catch blocks to unlock all the time. Instead, define a MutexLocker
class:
class MutexLocker
{
public:
MutexLocker(std::mullptr_t) = delete;
MutexLocker(Mutex* m): mutex_(m) {mutex_->lock();}
MutexLocker(MutexLocker const&) = delete;
MutexLocker& operator=(MutexLocker const&) = delete;
MutexLocker(MutexLocker&& l): mutex_(l.mutex_) {l.mutex_ = nullptr;}
MutexLocker& operator=(MutexLocker&& l)
{
mutex_ = l.mutex_,
l.mutex_ = nullptr;
return *this;
}
~MutexLocker() {if (mutex_) {mutex_->unlock()} };
private:
Mutex* mutex_;
};
Now, you can never forget to unlock a Mutex. The MutexLocker object cannot be copied, but you can transfer ownership. This is superior to anything you can do in C.
For formatting output, you can google "variadic template printf" which should give you some examples, e.g. on Wikipedia:
void printf(const char *s)
{
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
throw std::runtime_error("invalid format string: missing arguments");
}
}
std::cout << *s++;
}
}
template<typename T, typename... Args>
void printf(const char *s, T value, Args... args)
{
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
std::cout << value;
s += 2; // this only works on 2 characters format strings ( %d, %f, etc ). Fails miserably with %5.4f
printf(s, args...); // call even when *s == 0 to detect extra arguments
return;
}
}
std::cout << *s++;
}
}
Or you can use a library, e.g. boost::format
or probably thousands of other implementations. If it is only for logging, you could take a look at a logging framework, e.g. boost.log
.