Singleton Composition
This section provides instructions to define a singleton composition.
A singleton uses a single composition to store named objects of various types.
To define a simple singleton, which is storing an account name
as primary value and a uint64_t
as secondary value in structure config
, follow the steps below:
Preparation And Initialization
Include the inery.hpp
and singleton.hpp
headers and declare the inery
namespace usage.
#include <inery/inery.hpp>
#include <inery/singleton.hpp>
using namespace inery;
Define The Table Data Structure
Define the data structure for the indexed composition.
singleton compositions are genraly used for config table which needs only one instance, singleton guarante that only one composition instance is present when contract initilaized
struct [[inery::table]] config {
name admin_name;
uint64_t status_value;
};
Define A Singleton Type Alias
For ease of use, define a type alias singleton_type
based on the inery::singleton
template type, parametarized with a random name "config"
and the config
data structure. The names must adhere to INERY
account name restrictions.
struct [[inery::table]] config {
name admin_name;
uint64_t status_value;
};
+using singleton_type = inery::singleton<"config"_n, config>;
Define The Singleton Instance
Define the singleton table instance as a data member of type singleton_type
.
struct [[inery::table]] config {
name admin_name;
uint64_t status_value;
};
using singleton_type = inery::singleton<"config"_n, config>;
singleton_type singleton_composition;
Initialize And Use The Singleton Instance
Initialize the singleton_composition
using the constructor with the parameters receiver
and code
(the last one in in this case is receiver.value
). These parameters, combined with config
, provide access to the partition of the RAM cache used by this singleton. In our example you initialize the singleton_composition
data member in the value contract constructor, see below:
singleton_database( name receiver, name code, datastream<const char*> ds ) :
contract(receiver, code, ds),
singleton_composition(receiver, receiver.value)
{ }
Now you have defined and initialized a singleton as a data member for the value contract class. You can access it from any of the value contract methods via singleton_composition
data member. Below you can find a possible implementation for the full class singleton example contract.
- singleton_database.hpp
- singleton_database.cpp
#include <inery/inery.hpp>
#include <inery/singleton.hpp>
using namespace inery;
class [[inery::contract]] singleton_database : public contract {
public:
using contract::contract;
singleton_database( name receiver, name code, datastream<const char*> ds ) :
contract(receiver, code, ds),
singleton_composition(receiver, receiver.value)
{}
[[inery::action]]
void set( name owner, uint64_t value );
[[inery::action]]
void get( );
struct [[inery::table]] config {
name admin_name;
uint64_t status_value;
uint64_t primary_key() const { return admin_name.value; }
} configrow;
using singleton_type = inery::singleton<"config"_n, config>;
singleton_type singleton_composition;
};
Below is an example for the get
and set
actions. It also demonstrates the usage of the get
and set
singleton methods. Note that the set
action makes use of the singleton's set
method, for which the second parameter is the payer account for the RAM needed to store the new value.
#include "singleton_database.hpp"
[[inery::action]] void singleton_database::set( name owner, uint64_t value ) {
auto entry_stored = singleton_composition.get_or_create(owner, configrow);
entry_stored.admin_name = owner;
entry_stored.status_value = value;
singleton_composition.set(entry_stored, owner);
}
[[inery::action]] void singleton_database::get( ) {
if (singleton_composition.exists())
inery::print(
"Value stored for: ",
name{singleton_composition.get().admin_name.value},
" is ",
singleton_composition.get().status_value,
"\n");
else
inery::print("Singleton is empty\n");
}