Skip to content

Gaudel's Miscellany

  • Home
  • About

The access-by idiom in C++

20 April 2022
  • programming

The problem

Recently I had to write a cache manager in C++ for a problem-specific use case. To achieve that, I wrote a wrapper class around a map data structure. The cache manager had methods to store and access data using, surprise, keys! The important design decision about the cache manager class was that it hid the underlying map structure. When the time came to unit-test, I realized that a const reference to the underlying map object is needed, if desired to be rigorous about the tests.

I could think of two solutions: add a const method to the class that would return a reference to the underlying map, or add a friend class or a friend function thereby giving access to the private members to them. Neither option seemed to be a good approach to me.

  • Adding a const method to refer to the map would allow the user of the cache manager to bypass the checked-acces of the cached data. Which defeated the purpose of having the special cache manager in the first place.

  • On the other hand adding a friend function or a friend class to the class header just to be able to unit-test it, didn't feel right, as it implied the class is incomplete without implementing those friends.

Then I found this neat solution which I think, arguably, is an improved approach to the second option from above.

Solution

Let's look at it by writing an example. To keep things simpler, let's write a basic cache manager class which caches std::string data. The key used to store and access data will be of size_t type.

Code language
cpp
namespace nspc { // some namespace
class CacheManger {
private:
std::map<size_t, std::string> cache_;
public:
// constructors
// method: store data
// method: access data

template <typename T> struct access_by;
template <typename T> friend struct access_by;
};
} // namespace nspc

Now for unit-testing we sepcialize the access_by struct from the CacheManager class, which we designed to be a friend class.

Code language
cpp
namespace nspc { // specialization happens in the same namespace

struct ConstAccessToCache{};

template <>
struct CacheManger::access_by<ConstAccessToCache> {
// A function that returns a const reference to the
// CacheManger's private member cache_
[[nodiscard]] auto const& cache(CacheManger& cm) {
return cm.cache_;
}
};
} // namespace nspc

A demo main.cpp:

Code language
cpp
#include <string>
#include <map>
// CacheManger class definition from above
// CacheManger::access_by specialization from above
int main(int argc, char* argv[]) {

auto man = nspc::CacheManger{};

// access the cache_ member through the access_by specialization
auto accessor = decltype(man)::access_by<nspc::ConstAccessToCache>{};
auto const& cache = accessor.cache(man);

// now use cache for testing

return 0;
}

Tagged with

  • Cpp
  • Design

  • cpp
  • design
  • algorithm
  • programming

Theme

Follow me on:

  • GitHub

© Bimal Gaudel