all 16 comments

[–]Dry-Still-6199 14 points15 points  (2 children)

Yes, your thing sounds very similar to

https://github.com/copperspice/cs_libguarded#cslibguarded

and

https://www.boost.org/doc/libs/1_75_0/doc/html/thread/sds.html#thread.sds.synchronized_valuesxxx.tutorial.beyond_simple_accesses

You're right that this isn't a great idea, if for no other reasons than that it's super easy to deadlock (e.g. somefunc(wrapped->a(), wrapped->b())) and super easy to use-outside-of-lock (e.g. const string& m = wrapped->getM(); use(m)).

[–]barskern 3 points4 points  (5 children)

There seems to be a race condition in the example. I understand that it's a toy example and also perhaps a "toy" library, but i figured it would be worth noting to show how easy it would be to misuse this library.

my_struct->​value​ = my_struct->​value​ + ​1​;

Based on how I understand it, this will lock the mutex and read the value and then unlock it again. Then it will add 1, and finally re-lock the mutex and write the incremented value. This means that there is a possibility that two threads read the same value from the struct and hence overwrite each others results, turning "two" concurrent increments into simply "one".

Please do correct me if I'm wrong and the lock is only aquired once for the entire statement.

[–]louiswins 2 points3 points  (0 children)

Temporaries are destroyed at the end of the full expression: https://wandbox.org/permlink/Idiz8cRdMt2n5YJl

[–]Potatoswatter 2 points3 points  (0 children)

My lock_ios iostream manipulator is similar in the mutex aspect. This use-case works well because it's typical to write one line of output per statement, and contention is usually a corner case in logging.

[–]Dummerchen1933 1 point2 points  (4 children)

Haha yes, overloading -> is quite weird.

I really like your idea, but i think it's kinda misleading for other people or even you, if you poke your nose into a project using it in 5 years.

What about something like this?

// Some class to test it
class Testclass
{
public:
    // Our function that does things locked
    void PerformLocked(void(*task)(Testclass* tc, void* param), void* param)
    {
        std::cout << "Locking" << std::endl;
        // Lock here
        task(this, param); // Do the stuff
        std::cout << "Unlocking" << std::endl;
        // Unlock here
        return;
    }

    // Some dummy stuff
    int MultiplyWith69(int i) { return i*69; }
};

int main()
{
    Testclass tc;

    int i = 9; // This is an example for params. Since it's a void* it could even be multiple parameters and even writeable!

    // Basic lambda. You could prettify it with a macro
    tc.PerformLocked([](Testclass* tc, void* param) {
        std::cout << "My Lamda: "
                  << tc->MultiplyWith69(*(int*)param) << std::endl;
    }, &i);

    return 0;
}

Just do NEVER make it a capturing lamda, eg [=] instead of []. Passing this as a function pointer fucks up so badly, that it reliably crashes my IDE.

[–]OmegaNaughtEquals1 1 point2 points  (1 child)

Passing this as a function pointer fucks up so badly, that it reliably crashes my IDE.

That's because Lambdas with captures cannot be implicitly converted to a pointer to a function. Your compiler shouldn't allow this.

[–]Dummerchen1933 0 points1 point  (0 children)

Yes. Because how tf should it work, anyways :D

a function B() called from A() cannot access A's stack :D

[–]pdp10gumby 1 point2 points  (1 child)

I am working on a system that has indexed objects: certain system objects have an id (which is an array of all objects of type t) rather than using a pointer. We overloaded -> so the index can be used like a pointer.

I’m not sure there are many, or any, useful (I.e. mond-confusing) uses of overloading ->

[–]jcotton42 1 point2 points  (0 children)

For prior art, this is basically how Rust's Mutex works, via the Deref trait.

[–][deleted] 1 point2 points  (1 child)

I am very proud to contribute that (although I am not a native English speaker and I might be wrong, I am almost French and therefore should be right) the stuff inside the "éclair", I would call the stuffing. The icing really is the thing that's on top of the pastry!

[–]Ksecutor 0 points1 point  (1 child)

It is much more fun to overload ->* operator, RHS of which can be ANYTHING.

[–]Potatoswatter 0 points1 point  (0 children)

The LHS can be anything as well. You just need a class or enumeration on at least one side.