2

I'm developing a C++ library that will allow me to set value to POD only once (something analogous to flutter's final keyword.
In my application their are several configuration that are only set once (during initialization) and remain valid of the life-time of the program.
I'm using std::once and std::call_once for the same.
I've hit a snag whereby I'm unable to check if the std::call_once has been executed.
I was think of adding std::atomic_bool to track the status of std::call_once.
I was wondering if their is a better alternative than boolean flag where by I can use something associated std::once_flag object to get the status of std::call_once execution.

Code:

#pragma once
#include <mutex>

class DataNotSetException: public std::exception
{
public:
    const char* message = "Data Not Set before Get Operation";
    char* what()
    {
        return const_cast<char *>(this->message);
    }
};

template<typename T>
class SetOnce
{
private:
    T data;
    std::once_flag flag;
    void do_once(const T& data);
public:
    T get(void) const;
    void set(const T& data);
    bool is_set(void) const;

};

template<typename T>
inline void SetOnce<T>::do_once(const T& data)
{
    this->data = data;
}

//If data is not been set throw data not set exception (DataNotSetException)
template<typename T>
inline T SetOnce<T>::get(void) const
{
    if (this->is_set() == true)
    {
        return this->data;
    }
    else
    {
        throw DataNotSetException();
    }
}

template<typename T>
inline void SetOnce<T>::set(const T& data)
{
    std::call_once(this->flag, this->do_once, data);
}

template<typename T>
inline bool SetOnce<T>::is_set(void) const
{
    //How to check if the call_once has been executed
}

Edit

I'm working on a real-time system and hence wish to avoid delay associated blocking (busy-wait) mutex calls on get operation.
The get operation is expected to have low response time and high frequency of execution.

Aamir
  • 1,974
  • 1
  • 14
  • 18
Dark Sorrow
  • 1,681
  • 14
  • 37
  • 3
    Why not just use `std::call_once`? If the function have been called already, it won't be called again. Having an explicit check kind of defeats its purpose. – Some programmer dude Aug 07 '23 at 09:35
  • In my `T SetOnce::get(void)` method I wish to throw exception `DataNotSetException` if data is not set before the program tries to access the data. – Dark Sorrow Aug 07 '23 at 09:37
  • @PepijnKramer Threads will be created in the main function. – Dark Sorrow Aug 07 '23 at 09:37
  • 2
    A bit weird this packaging in `SetOnce` class. But one of the ways to signal "resources not initialized" is to use `std::optional`. – pptaszni Aug 07 '23 at 09:38
  • 1
    `std::call_once` is only to call the function once. If you want to check if it has been called you can use a `std::atomic` that is set inside `do_once` – 463035818_is_not_an_ai Aug 07 '23 at 09:40
  • 2
    I think `std::call_once` and `std::once_flag` is not the correct tool here. I think a mutex and a boolean flag would be enough to satisfy your requirement. When calling `set`, acquire a lock from the mutex, check the flag, and if not set then to the actual setting of the data and set the flag. In `get` acquire the lock from the mutex, and check the flag, if it's set then get the data, unlock the mutex and return the data, otherwise unlock the mutex and throw the exception. Much more straightforward. – Some programmer dude Aug 07 '23 at 09:45
  • @Someprogrammerdude What you have suggested was my first thought. However I'm working on a real-time system and I wish to avoid mutex especially in the `get` method hence I moved to `std::call_once` in `set` method. My `get` method is expected to be called at least once every milli-second and I would like to avoid delay associated blocking mutex. – Dark Sorrow Aug 07 '23 at 10:06
  • 1
    That's quite important information that should be in the question itself. Please [edit] your question to include it. – Some programmer dude Aug 07 '23 at 10:12
  • 1
    On a totally different note, the exception [`what`](https://en.cppreference.com/w/cpp/error/exception/what) function is supposed to return a `const char*`. So you can just return the string literal itself, and you don't need a cast. – Some programmer dude Aug 07 '23 at 10:13

1 Answers1

3

It is not that pretty but you could use std::call_once itself to perform the check:

bool check_once(std::once_flag& flag)
{
    try{
        std::call_once(flag,[](){throw 42;});
        return true;
    }
    catch(const int&){
        return false;
    }
}
  • If a function was already called before, this call does nothing -> true is returned.
  • If no function was called before, our function is called and throws unconditionally, this ensures the flag is not set and the intended call can still happen later.

Personally, I would go with a custom std::atomic_bool that you set at the end of your call.

Quimby
  • 17,735
  • 4
  • 35
  • 55
  • 3
    I would definitely prefer the atomic flag. Your solution has the cost of checking a atomic flag *and* throwing an exception when only the flag is needed – 463035818_is_not_an_ai Aug 07 '23 at 09:43
  • @Quimby Thanks for your suggestion but it seems more complicated (time complex) compared to using atomic_bool or std::optional. – Dark Sorrow Aug 07 '23 at 09:46
  • @463035818_is_not_an_ai Agreed and also the cost of `std::call_once` machinery. – Quimby Aug 07 '23 at 09:47
  • 1
    @DarkSorrow It is, careful that `std::optional` is not thread-safe. This could be useful if you have many flags and pairing them with `std::atomic_bool` would be too much hassle. Seems like `std::once_flag` having `operator bool` would not be too bad of an idea. – Quimby Aug 07 '23 at 09:48