Skip to main content
Clarify the smart pointer suggestion for asker
Source Link
Toby Speight
  • 88.7k
  • 14
  • 104
  • 327

This looks odd:

LFQueue(std::size_t size) : write_id(0), read_id(0), capacity_(size) {
    buf = new T[capacity_];
}

Why is buf not initialised in the initializer list, like the other members?

More importantly, this default-constructs capacity_ objects. That might just be inefficient, but when T is not default-constructible, it makes the class unusable.

We should really consider allocating uninitialised storage for the elements, and constructing them in place as required. That's what standard containers do.

Also, instead of using raw new, consider achanging to std::unique_ptrunique_ptr<T[]> const buf to manage the storage. Then there would be no need for a user-defined constructor.

Or just delegate managing the storage to a std::deque and let this class have the Single Responsibility of managing the concurrent access. I'd be surprised if that even costs anything in performance when compiled with -Ofast.


The write() and read() functions are hard to use. Instead of blocking until the operation is possible, they just return false, meaning that the user has to manage retrying (likely leading to spinning at one end or the other instead of polite waiting).


Prefer to use standard collection names for functions such as emplace_back() and empty(), and provide the normal member type aliases such as value_type. That makes it easier for readers to comprehend, and for users to switch implementations as necessary.


I haven't looked at the benchmarking code, because there's no point measuring performance until the interface is finalised.

This looks odd:

LFQueue(std::size_t size) : write_id(0), read_id(0), capacity_(size) {
    buf = new T[capacity_];
}

Why is buf not initialised in the initializer list, like the other members?

More importantly, this default-constructs capacity_ objects. That might just be inefficient, but when T is not default-constructible, it makes the class unusable.

We should really consider allocating uninitialised storage for the elements, and constructing them in place as required. That's what standard containers do.

Also, instead of using new, consider a std::unique_ptr const to manage the storage. Then there would be no need for a user-defined constructor.

Or just delegate managing the storage to a std::deque and let this class have the Single Responsibility of managing the concurrent access. I'd be surprised if that even costs anything in performance when compiled with -Ofast.


The write() and read() functions are hard to use. Instead of blocking until the operation is possible, they just return false, meaning that the user has to manage retrying (likely leading to spinning at one end or the other instead of polite waiting).


Prefer to use standard collection names for functions such as emplace_back() and empty(), and provide the normal member type aliases such as value_type. That makes it easier for readers to comprehend, and for users to switch implementations as necessary.


I haven't looked at the benchmarking code, because there's no point measuring performance until the interface is finalised.

This looks odd:

LFQueue(std::size_t size) : write_id(0), read_id(0), capacity_(size) {
    buf = new T[capacity_];
}

Why is buf not initialised in the initializer list, like the other members?

More importantly, this default-constructs capacity_ objects. That might just be inefficient, but when T is not default-constructible, it makes the class unusable.

We should really consider allocating uninitialised storage for the elements, and constructing them in place as required. That's what standard containers do.

Also, instead of using raw new, consider changing to std::unique_ptr<T[]> const buf to manage the storage. Then there would be no need for a user-defined constructor.

Or just delegate managing the storage to a std::deque and let this class have the Single Responsibility of managing the concurrent access. I'd be surprised if that even costs anything in performance when compiled with -Ofast.


The write() and read() functions are hard to use. Instead of blocking until the operation is possible, they just return false, meaning that the user has to manage retrying (likely leading to spinning at one end or the other instead of polite waiting).


Prefer to use standard collection names for functions such as emplace_back() and empty(), and provide the normal member type aliases such as value_type. That makes it easier for readers to comprehend, and for users to switch implementations as necessary.


I haven't looked at the benchmarking code, because there's no point measuring performance until the interface is finalised.

Rollback to Revision 1
Source Link
Toby Speight
  • 88.7k
  • 14
  • 104
  • 327

This looks odd:

LFQueue(std::size_t size) : write_id(0), read_id(0), capacity_(size) {
    buf = new T[capacity_];
}

Why is buf not initialised in the initializer list, like the other members?

More importantly, this default-constructs capacity_ objects. That might just be inefficient, but when T is not default-constructible, it makes the class unusable.

We should really consider allocating uninitialised storage for the elements, and constructing them in place as required. That's what standard containers do.

Also, instead of using new, consider a std::unique_ptr const to manage the storage. Then there would be no need for a user-defined constructor.

Or just delegate managing the storage to a std::queuedeque and let this class have the Single Responsibility of managing the concurrent access. I'd be surprised if that even costs anything in performance when compiled with -Ofast.


The write() and read() functions are hard to use. Instead of blocking until the operation is possible, they just return false, meaning that the user has to manage retrying (likely leading to spinning at one end or the other instead of polite waiting).


Prefer to use standard collection names for functions such as emplace_back() and empty(), and provide the normal member type aliases such as value_type. That makes it easier for readers to comprehend, and for users to switch implementations as necessary.


I haven't looked at the benchmarking code, because there's no point measuring performance until the interface is finalised.

This looks odd:

LFQueue(std::size_t size) : write_id(0), read_id(0), capacity_(size) {
    buf = new T[capacity_];
}

Why is buf not initialised in the initializer list, like the other members?

More importantly, this default-constructs capacity_ objects. That might just be inefficient, but when T is not default-constructible, it makes the class unusable.

We should really consider allocating uninitialised storage for the elements, and constructing them in place as required. That's what standard containers do.

Also, instead of using new, consider a std::unique_ptr const to manage the storage. Then there would be no need for a user-defined constructor.

Or just delegate managing the storage to a std::queue and let this class have the Single Responsibility of managing the concurrent access. I'd be surprised if that even costs anything in performance when compiled with -Ofast.


The write() and read() functions are hard to use. Instead of blocking until the operation is possible, they just return false, meaning that the user has to manage retrying (likely leading to spinning at one end or the other instead of polite waiting).


Prefer to use standard collection names for functions such as emplace_back() and empty(), and provide the normal member type aliases such as value_type. That makes it easier for readers to comprehend, and for users to switch implementations as necessary.


I haven't looked at the benchmarking code, because there's no point measuring performance until the interface is finalised.

This looks odd:

LFQueue(std::size_t size) : write_id(0), read_id(0), capacity_(size) {
    buf = new T[capacity_];
}

Why is buf not initialised in the initializer list, like the other members?

More importantly, this default-constructs capacity_ objects. That might just be inefficient, but when T is not default-constructible, it makes the class unusable.

We should really consider allocating uninitialised storage for the elements, and constructing them in place as required. That's what standard containers do.

Also, instead of using new, consider a std::unique_ptr const to manage the storage. Then there would be no need for a user-defined constructor.

Or just delegate managing the storage to a std::deque and let this class have the Single Responsibility of managing the concurrent access. I'd be surprised if that even costs anything in performance when compiled with -Ofast.


The write() and read() functions are hard to use. Instead of blocking until the operation is possible, they just return false, meaning that the user has to manage retrying (likely leading to spinning at one end or the other instead of polite waiting).


Prefer to use standard collection names for functions such as emplace_back() and empty(), and provide the normal member type aliases such as value_type. That makes it easier for readers to comprehend, and for users to switch implementations as necessary.


I haven't looked at the benchmarking code, because there's no point measuring performance until the interface is finalised.

edited body
Source Link
Toby Speight
  • 88.7k
  • 14
  • 104
  • 327

This looks odd:

LFQueue(std::size_t size) : write_id(0), read_id(0), capacity_(size) {
    buf = new T[capacity_];
}

Why is buf not initialised in the initializer list, like the other members?

More importantly, this default-constructs capacity_ objects. That might just be inefficient, but when T is not default-constructible, it makes the class unusable.

We should really consider allocating uninitialised storage for the elements, and constructing them in place as required. That's what standard containers do.

Also, instead of using new, consider a std::unique_ptr const to manage the storage. Then there would be no need for a user-defined constructor.

Or just delegate managing the storage to a std::dequequeue and let this class have the Single Responsibility of managing the concurrent access. I'd be surprised if that even costs anything in performance when compiled with -Ofast.


The write() and read() functions are hard to use. Instead of blocking until the operation is possible, they just return false, meaning that the user has to manage retrying (likely leading to spinning at one end or the other instead of polite waiting).


Prefer to use standard collection names for functions such as emplace_back() and empty(), and provide the normal member type aliases such as value_type. That makes it easier for readers to comprehend, and for users to switch implementations as necessary.


I haven't looked at the benchmarking code, because there's no point measuring performance until the interface is finalised.

This looks odd:

LFQueue(std::size_t size) : write_id(0), read_id(0), capacity_(size) {
    buf = new T[capacity_];
}

Why is buf not initialised in the initializer list, like the other members?

More importantly, this default-constructs capacity_ objects. That might just be inefficient, but when T is not default-constructible, it makes the class unusable.

We should really consider allocating uninitialised storage for the elements, and constructing them in place as required. That's what standard containers do.

Also, instead of using new, consider a std::unique_ptr const to manage the storage. Then there would be no need for a user-defined constructor.

Or just delegate managing the storage to a std::deque and let this class have the Single Responsibility of managing the concurrent access. I'd be surprised if that even costs anything in performance when compiled with -Ofast.


The write() and read() functions are hard to use. Instead of blocking until the operation is possible, they just return false, meaning that the user has to manage retrying (likely leading to spinning at one end or the other instead of polite waiting).


Prefer to use standard collection names for functions such as emplace_back() and empty(), and provide the normal member type aliases such as value_type. That makes it easier for readers to comprehend, and for users to switch implementations as necessary.


I haven't looked at the benchmarking code, because there's no point measuring performance until the interface is finalised.

This looks odd:

LFQueue(std::size_t size) : write_id(0), read_id(0), capacity_(size) {
    buf = new T[capacity_];
}

Why is buf not initialised in the initializer list, like the other members?

More importantly, this default-constructs capacity_ objects. That might just be inefficient, but when T is not default-constructible, it makes the class unusable.

We should really consider allocating uninitialised storage for the elements, and constructing them in place as required. That's what standard containers do.

Also, instead of using new, consider a std::unique_ptr const to manage the storage. Then there would be no need for a user-defined constructor.

Or just delegate managing the storage to a std::queue and let this class have the Single Responsibility of managing the concurrent access. I'd be surprised if that even costs anything in performance when compiled with -Ofast.


The write() and read() functions are hard to use. Instead of blocking until the operation is possible, they just return false, meaning that the user has to manage retrying (likely leading to spinning at one end or the other instead of polite waiting).


Prefer to use standard collection names for functions such as emplace_back() and empty(), and provide the normal member type aliases such as value_type. That makes it easier for readers to comprehend, and for users to switch implementations as necessary.


I haven't looked at the benchmarking code, because there's no point measuring performance until the interface is finalised.

Source Link
Toby Speight
  • 88.7k
  • 14
  • 104
  • 327
Loading