LouisCharlesC / safe

Header only read/write wrapper for mutexes and locks.
MIT License
147 stars 11 forks source link

help with enforcing read-only access #29

Closed tonym247 closed 18 hours ago

tonym247 commented 1 year ago

Hi - struggling with this feature. The readme reference a file /src/traits.h but I don't see this in my clone. I tried the example but hit error 'error: ‘AccessTraits’ is not a class template' .. Have I missed something?

LouisCharlesC commented 1 year ago

Dear Lord, the readme was wrong. I just fixed it, thanks!

Now for the other problem, the code in the accessmode.h file lives in the namespace safe, so it does not have to specify safe:: before AccessTraits.

Can you try by using safe::AccessTraits rather than a plain AccessTraits ?

tonym247 commented 1 year ago

my understanding of C++ templates is not great and I am relying on the code examples to get through ... is it possible to provide a simple code example whereby read-only access is enforced? - also the section mentions "Use the trait class safe::LockTraits to customize this behavior" .. not sure what that is.

LouisCharlesC commented 1 year ago

Readme fixed again, thanks again!

Ok, well the code example does do it. It's just that since it is enclosed by namespace safe {} then the safe:: is not necessary. You can get the same effect with using namespace safe;, just like when you use using namespace std you don't need to prefix all std things with std::. Anyways, I'll add safe:: in the readme, no problem.

Here's an example, but it's exactly the same code as in the readme, execpt for the safe:: thing:

template<typename T> struct SomeClass;

template<typename T>
struct safe::AccessTraits<SomeClass<T>>
{
    static constexpr bool IsReadOnly = true;
};

Now, if your own type has more than one template argument, you'll need to change the code accordingly:

template<typename T, typename U, typename V> struct SomeClass;

template<typename T, typename U, typename V>
struct safe::AccessTraits<SomeClass<T, U, V>>
{
    static constexpr bool IsReadOnly = true;
};

You should be able to copy-paste these examples.

And if things don't work out, I might need a snippet of code from your side to understand the problem.

tonym247 commented 1 year ago

thanks for the help but I am still not getting it ...

Given this trivial example How do I use the template? .. (I expect the compiler will error for the attempt to Write once the template is applied)

class FileOwner {
public:
    FileOwner() {}
    ~FileOwner() {}
    const std::string& getOwner() const { return owner_; }
    void setOwner(std::string owner) { owner_ = owner; }
private:
  std::string owner_;
};

int main() {
    using SafeFile = safe::Safe<FileOwner>;
    SafeFile safeFile;
    {
        safe::WriteAccess<SafeFile> value(safeFile); // this locks the mutex and gives you access
        value->setOwner("testuser"); // access the value using pointer dereference: * and ->
    }
    auto value = safeFile.readAccess();
    std::cout << "group: " << value->getOwner() << std::endl;
    return EXIT_SUCCESS;
}
LouisCharlesC commented 1 year ago

Ok, I should have asked for a snippet earlier :smile: From what I understand, you just want to use the library in the most standard way, no template needed, no specialization.

safe works seamlessly with most value types. The value type is the thing you want to protect with a mutex. In your example, the value type is FileOwner. You can see most of safe types as pointers or references. The object safeFile can give you access to your FileOwner object just like a pointer. If your FileOwner object was behind a pointer and you wanted to prevent writes to it through the pointer, you would simply declare you pointer const.

In your example, if you declare your safeFile like so; const SafeFile safeFile;, the safe object will be const and will only allow read access to your value type.

Is it clearer ?

tonym247 commented 1 year ago

Yes thank you - However, let me frame the question in a different way. If you consider the snippet and I have multiple threads 'reading' the value of FileOwner. I want each thread to perform the read concurrently. My understanding is that the specialisation will force the use of the 'shared' locks. How do I use the template parameters to do this?

LouisCharlesC commented 1 year ago

Hello,

There is no way in safe to force the use of a specific lock. You can enforce read-only access (but the lock used to make that access it still chosen by the calling code) with const. And you can make it so that when using a certain lock type, the only possible accesses are read-only, using AccessTraits.

It should be possible to change the library to allow enforcing a specific lock, it's not planned for now.

LouisCharlesC commented 1 year ago

Can we consider this issue closed, or can I do something else for you ?

tonym247 commented 1 year ago

Yes please consider it closed. Thanks for your help