Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I'm writing a C++ wrapper for my C library (my C++ skills are a bit rusty) and I'm wondering what's preferable: allowing user to construct objects themselves or give them objects via getters?

What would be idiomatic C++ equivalent of that C function?

result* process_foo(library_handle*, foo*);

a constructor like this:

Result *res = new Result(library_handle, foo);

or a getter like this? (that basically wraps the above code):

Result *res = library->get_result(foo);

Does the answer change if creation of the result could fail?

Getter could return NULL, but what should constructor version do on failure? AFAIK some people don't like exceptions. An extra method such as init() or is_valid() doesn't seem elegant.

OTOH the Result object sticks to RAII, it's tiny with inline methods, so it might be good candidate for stack allocation.

share|improve this question

4 Answers 4

I would either return a shared_ptr or custom smart pointer, or allow automatic (stack) creation of objects. I might wrap a shared pointer in a result object if the result was large.

Whether it is the library which is processing the foo or a foo which is being processed using resources from the library depends on information you haven't provided, but typically you'd expect something like:

class Foo {
    public:
        Result Process ( Library& library );
};

or, if library is fixed for the lifetime of the Foo

class Foo {
    public:
        explicit Foo ( Library& library );
        Result Process ();
};

either Result is a small struct, or if it is large then I would have it hold a smart pointer to the data.

I would throw an exception if Process failed. Given new throws an exception on failure, as do many standard C++ functions, people who don't like exceptions probably shouldn't be working in C++.

If it is expected that process could return an invalid result, then instead of an exception I would add a function to the result to test its validity, and possibly a conversion to bool so you could write

Result res ( foo.Process() );

if ( res.IsValid() ) {
    ...
}

or

if ( res ) {
    ...
}

Accessing data from an invalid result would throw an exception.

share|improve this answer
1  
Just FYI, a lot of people use C++ without exceptions. If you're coding with Qt, chances are you're not: stackoverflow.com/questions/5548951/… Also, check out this question programmers.stackexchange.com/questions/113479/… –  MrFox Mar 15 '13 at 13:40
3  
Also, please don't return shared pointers from libraries. Let the users decide what type of memory management they want to implement (boost? C++11's unique_pointer - what if my compiler doesn't support it? QPointer?). Straight up Object * is the most portable and polite way to go when shipping libraries. Let the user wrap it in whatever they want to use. –  MrFox Mar 15 '13 at 13:45
2  
@MrFox so you're suggesting that Result would look like struct Result { char* data; }; and it's up to the client to either delete or free or delete[] data, rather than my suggestion of returning a result with a shared pointer, such as struct Result { boost::scoped_array<char> data; }; ? Do you also not use std::string in case that isn't supported? –  Pete Kirkham Mar 15 '13 at 15:27
2  
@MrFox using std::string is the same case - the library can choose to manage the array the string wraps in whatever way it sees best - e.g copy on write rather than always copying. The problem with returning a raw pointer to the data is that the client of the library does not know how to release that pointer - if it's a copy on write object then deleting it will result in undefined behaviour, as will deleting it if the library was compiled with a different runtime. I'm not familiar with QT, but examples showing use of QPointer show the client code using new as well, so don't hit the issue. –  Pete Kirkham Mar 16 '13 at 13:36
1  
Returning a smart pointer? That's an automatic "no" for me to use the library. –  l46kok Mar 19 '13 at 11:13

Constructor is not expected to do much processing, generally, so I would steer clear of that at least.

Result object should be just the result, created and returned by some other class (or just function, if you're interfacing C). Depending on how much stuff there will be in the result, you can construct that either with constructor parameters, or if there are too many, then having constructor with no parameters (or mandatory parameters), and then set all (or optional) parameters of Result instance after construction, before returning it.

If the library->get_result(foo) involves actual processing and not just getting current result value, then I'd use alternative name. At least to me "get" suggests it is a lightweight operation.


So, in this case probably one of these (C++11 assumed), first declaration of a function (C++ is multi-paradigm language, nothing wrong with functions especially when interfacing with C libs):

namespace library {
   class Result {...}
   std::unique_ptr<Result> process(library_handle*, foo*); // note: uses move semantics
}

//usage
auto result = library::process(handle, foo)

Also note how I removed "foo" from function name. C++ supports overloading, no point in duplicating information, which is already given by type of 2nd argument.

Or a more object-oriented approach:

namespace library {
    class Result {...}
    class Library {
        Library(library_handle*); // constructor
        std::unique_ptr<Result> process(foo*); // library handle already known
    }
}

//usage
auto result = library->process(foo);

In that last example, result might also get stored as member variable in Library class, in which case you would have getter for it. Also, depending on who owns the Result and how big it is, you should consider passing it by value, after learning about C++11 move semantics which would avoid a lot fo copies.


General thoughts: if you are unsure about your C++, consider providing just a very thin C++ wrapper around C, basically just some convenience "value" classes like that Result, and plain functions in a namespace, almost 1:1 mapping to your C API. It might not be as nice as a good OOP C++ API, but it will be a lot better than crappy C++ API designed while learning modern C++ ;)

share|improve this answer

You dismissed the constructor + init method approach, but it's not necessarily a bad choice. In fact, Cocoa (the Objective C setup used widely in iOS/MacOS programming) uses the post-allocation "init" approach everywhere, so there is a widely used and successful precedent for it. If you have a substantial and not-guaranteed initialization, and you are reluctant to throw exceptions (and I would be reluctant as well!), then maybe you actually do want to go the "init" route.

Here's a description of Cocoa's init pattern, I know you're doing C++, but it might be informative by way of analogy:

https://developer.apple.com/library/mac/#documentation/General/Conceptual/CocoaEncyclopedia/Initialization/Initialization.html#//apple_ref/doc/uid/TP40010810-CH6

share|improve this answer

There is another alternative solution to this problem. Instead of using getter and returning pointer to object, you could use a getter and return handle to the object. For example:

struct H { int id; };
H Library::get_result(int i);
H h = library->get_result(10);

Then Library::get_result() would keep std::vector of the actual objects and return handle:

   H Library::get_result(int i) {
     vec.push_back(new Result(i)); // or find existing object
     H h;
     h.id = vec.size()-1;
     return h;
   }

Most people would use unique_ptr<> as the handle, but I wanted to give another alternative solution, which do not keep ownership of the object in the handle, but instead the std::vector keeps ownership.

Good thing about struct H { int id; } style handles is that they are freely copyable (since it's just an int), and passing around the handle in your code becomes considerably easier when you can pass copies of the int instead of passing actual objects. Also it's not possible to modify temporary copies of the object and lose the modifications when passing around objects in your code. (if you use the new-solution directly, someone is going to create it on stack.)

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.