How it works...

std::shared_ptr is used to manage a pointer when more than one thing must own the pointer for the application to execute properly. Suppose, however, that you provide an API that must accept an integer pointer, as follows:

void execute_threads(int *ptr);

The preceding API suggests that whoever calls this function owns the integer pointer. That is, whoever calls this function is required to allocate the integer pointer, as well as delete it once the function is complete. If, however, we intend for the preceding API to own the pointer, we really should write this API as follows:

void execute_threads(std::unique_ptr<int> ptr);

This API says, please allocate me an integer pointer, but I own it once it's passed and will ensure it is deleted when needed. Now, suppose this function will use this pointer in a one-to-many ownership scenario. What do you do? You could write your API as follows:

void execute_threads(std::shared_ptr<int> ptr);

This would, however, prevent your API from optimizing the one-to-many relationship in the future (that is, if you were able to remove this relationship in the future, you would still be stuck with std::shared_ptr, even though it is suboptimal without having to modify the API's function signature).

To solve this, the C++ APIs provide the ability to convert a std::unique_ptr into a std::shared_ptr, as follows:

std::atomic<int> count;

void
inc(std::shared_ptr<int> val)
{
count += *val;
}

Suppose we have an internal function that, for now, takes an integer pointer as a std::shared_ptr, uses its value to increment count, and executes it as a thread. Then, we provide a public API for it to use this internal function, as follows:

void
execute_threads(std::unique_ptr<int> ptr)
{
std::array<std::thread, 42> threads;
auto shared = std::shared_ptr<int>(std::move(ptr));

for (auto &thread : threads) {
thread = std::thread{inc, shared};
}

for (auto &thread : threads) {
thread.join();
}
}

As shown in the preceding code, our API claims ownership of a previously allocated integer pointer. Then, it creates a series of threads, executing each one and waiting for each thread to finish. The problem is that our internal function requires a std::shared_ptr (for example, maybe this internal function is used somewhere else in the code where there is a one-to-many ownership scenario that we cannot remove at the moment).

To prevent the need to define our public API with std::shared_ptr, we can convert std::unique_ptr into std::shared_ptr by moving std::unique_ptr into a new std::shared_ptr and then calling our threads from there.

std::move() is required, as the only way to pass ownership of std::unique_ptr is through the use of std::move() (as only one std::unique_ptr can own the pointer at any given time).

Now, we can execute this public API as follows:

int main(void)
{
execute_threads(std::make_unique<int>(1));
std::cout << "count: " << count << ' ';

return 0;
}

This results in the following output:

In the future, we might be able to remove the need for std::shared_ptr and pass std::unique_ptr to our internal function using the get() function, and, when that time comes, we won't have to modify the public API.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset