Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Stroika::Foundation::Common::Property< T > Class Template Reference

#include <Property.h>

Inheritance diagram for Stroika::Foundation::Common::Property< T >:
Stroika::Foundation::Common::ReadOnlyProperty< T > Stroika::Foundation::Common::WriteOnlyProperty< remove_cvref_t< T > > Stroika::Foundation::Common::ExtendableProperty< optional< uint64_t > > Stroika::Foundation::Common::ExtendableProperty< optional< Stroika::Foundation::IO::Network::HTTP::ETag > > Stroika::Foundation::Common::ExtendableProperty< optional< TransferEncodings > > Stroika::Foundation::Common::ExtendableProperty< Stroika::Foundation::IO::Network::HTTP::Headers & > Stroika::Foundation::Common::ExtendableProperty< Status > Stroika::Foundation::Common::ExtendableProperty< tuple< Status, optional< Stroika::Foundation::Characters::String > > > Stroika::Foundation::Common::ExtendableProperty< optional< Stroika::Foundation::DataExchange::InternetMediaType > > Stroika::Foundation::Common::ExtendableProperty< T >

Public Types

using base_value_type = T
 base_value_type is T the type declared, and decayed_value_type is similar, but with the references etc removed (so can be set/stored)
 

Public Member Functions

 Property ()=delete
 
- Public Member Functions inherited from Stroika::Foundation::Common::ReadOnlyProperty< T >
 ReadOnlyProperty ()=delete
 
nonvirtual T Get () const
 
nonvirtual operator const T () const
 
nonvirtual const T operator() () const
 
- Public Member Functions inherited from Stroika::Foundation::Common::WriteOnlyProperty< remove_cvref_t< T > >
 WriteOnlyProperty ()=delete
 
nonvirtual WriteOnlyPropertyoperator= (ArgByValueType< remove_cvref_t< T > > value)
 
nonvirtual void Set (ArgByValueType< remove_cvref_t< T > > value)
 
nonvirtual void operator() (ArgByValueType< remove_cvref_t< T > > value)
 

Detailed Description

template<typename T>
class Stroika::Foundation::Common::Property< T >

Implement C#-like syntax for properties (syntactically like data members but backed by a getter and a setter function).

Properties are NOT movable, nor copy constructible: the data doesn't logically exist in the property itself, but in its relationship to some parent object; if it were copied, it might be copied TO some place that didn't have an appropriate enclosing object.

But, properties are ASSIGNABLE TO, because here we retain our getter/setter, and just treat assignment as copying the value.

Note
This has implications for classes that use Property objects: they will not be able to use X(const X&) = default; nor X(X&&) = default; Classes with properties can be copy constructed/move constructed, but just not automatically (memberwise). It wouldn't make sense to do memberwise because typically the property just 'knows how to find the data' stored elsewhere in the owning object.
Though this looks syntactically much like using a direct data member, it will likely have some performance overhead due to forcing the use of a std::function wrapping a lambda for each access to the underlying object of type T.
see base class ReadOnlyProperty and WriteOnlyProperty for details on APIs to read/write the underlying data.
New Stroika NAMING CONVENTION introduced for instance variables of type Property<...>, which is to prepend a 'p'. This convention is to emphasize that these names DONT work QUITE like fields, but only somewhat like fields.
Example Usage
struct Headers {
public:
Headers ();
Headers (const Headers& src);
Headers (Headers&& src);
nonvirtual Headers& operator= (const Headers& rhs) = default; // properties are assignable, so this is OK
nonvirtual Headers& operator = (Headers&& rhs);
Property<unsigned int> contentLength1; // all 3 refer to the private fContentLength_ field
Property<unsigned int> contentLength2;
Property<unsigned int> contentLength3;
private:
unsigned int fContentLength_{0};
};
Headers::Headers ()
// Can implement getter/setters with this capture (wastes a bit of space)
: contentLength1{
[this] ([[maybe_unused]] const auto* property) {
return fContentLength_;
},
[this] ([[maybe_unused]] auto* property, const auto& contentLength) {
fContentLength_ = contentLength;
}}
// Can implement getter/setters with Memory::GetObjectOwningField - to save space, but counts on exact
// storage layout and not totally legal with non- is_standard_layout<> - see Memory::GetObjectOwningField
, contentLength2{
[] (const auto* property) {
const Headers* headerObj = Memory::GetObjectOwningField (property, &Headers::contentLength2);
return headerObj->fContentLength_;
},
[] (auto* property, auto contentLength) {
Headers* headerObj = Memory::GetObjectOwningField (property, &Headers::contentLength2);
headerObj->fContentLength_ = contentLength;
}}
// Use stroika #define to decide which strategy to use
, contentLength3{
[qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]]const auto* property) {
const Headers* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &Headers::contentLength3);
return thisObj->fContentLength_;
},
[qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]]auto* property, auto contentLength) {
Headers* thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &Headers::contentLength3);
thisObj->fContentLength_ = contentLength;
}}
{
}
Headers::Headers (const Headers& src)
: Headers{} // do default initialization of properties
{
// NOTE - cannot INITIALIZE properties with src.Properties values since they are not copy constructible
// but they are assignable, so do that
contentLength1 = src.contentLength1;
contentLength2 = src.contentLength2;
// COULD EITHER initialize fContentLength_ or contentLength1/contentLength2 - but no need to do both
}
Headers::Headers (Headers&& src)
: Headers{} // do default initialization of properties
{
// NOTE - cannot MOVE properties with src.Properties values since they are not copy constructible
// but they are assignable, so do that
contentLength1 = src.contentLength1;
contentLength2 = src.contentLength2;
// COULD EITHER initialize fContentLength_ or contentLength1/contentLength2 - but no need to do both
}
Headers& Headers::operator= (Headers&& rhs)
{
// Could copy either properties or underlying field - no matter which
fContentLength_ = rhs.fContentLength_;
return *this;
}
//....
Headers h;
Assert (h.contentLength1 == 0);
h.contentLength1 = 2;
Assert (h.contentLength2 == 2);
h.contentLength2 = 4;
Assert (h.contentLength1 == 4);
Headers h2 = h;
Assert (h2.contentLength1 == 4);
h.contentLength2 = 5;
Assert (h.contentLength1 == 5);
Assert (h2.contentLength1 == 4);

Another important scenario, is where you have a complex, and want to update it. Properties only allow for getters and setters, not updaters. The simple solution to this is to have the Property return a REFERENCE to the object in question.

Example Usage (EXTENDING the Header example above)
// HTTP 'Response' object
class Response {
public:
Response () = delete;
Response (const Response&) = delete;
Response (Response&& src);
Response (const IO::Network::Socket::Ptr& s, const Streams::OutputStream::Ptr<byte>& outStream, const optional<InternetMediaType>& ct = nullopt);
~Response () = default;
nonvirtual Response& operator= (const Response&) = delete;
public:
public:
...
// then the ABOVE checks/assignments become
Response& r = get_from_somewhere();
Assert (r.headers().contentLength1 == 0);
r.rwHeaders().contentLength1 = 2;
Assert (r.headers().contentLength2 == 2);
h.contentLength2 = 4;
Assert (r.headers().contentLength1 == 4);
Headers h2 = r.headers;
Assert (h2.contentLength1 == 4);
r.rwHeaders().contentLength2 = 5;
Assert (r.headers().contentLength1 == 5);
Assert (h2.contentLength1 == 4);
a smart pointer wrapper (like shared_ptr <_IRep>).
Definition Socket.h:178
OutputStream<>::Ptr is Smart pointer to a stream-based sink of data.
Note
when using Properties, its often helpful to combine (for thread safety checking) with Debug::AssertExternallySynchronizedMutex and SetAssertExternallySynchronizedMutexContext ()
See also
See the example usage (above outlined) in Frameworks/WebServer/Request.h, Message.h, Response.h, and Foundation/IO/Network/HTTP/Headers
Note
Thread-Safety SAME AS T/GETTER/SETTER - all methods have exactly the thread safety of the underlying GETTER/SETTER

Definition at line 406 of file Property.h.

Constructor & Destructor Documentation

◆ Property()

template<typename T >
Stroika::Foundation::Common::Property< T >::Property ( )
delete

Properties are NOT movable, nor copy constructible: the data doesn't logically exist in the property itself, but in its relationship to some parent object; if it were copied, it might be copied TO some place that didn't have an appropriate enclosing object.


The documentation for this class was generated from the following files: