Skip to main content

Singleton

Overview

This guide provides instructions to define a singleton.

Procedure

A singleton uses a single multi-index table 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 datatablele, follow the steps below:

1. 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;

2. Define The Table Data Structure

Define the data structure for the multi-index table:

struct [[inery::table]] datatablele {
name primary_value;
uint64_t secondary_value;
};

3. 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 "datatablele" and the datatablele data structure. The names must adhere to INERY account name restrictions.

struct [[inery::table]] datatablele {
name primary_value;
uint64_t secondary_value;
};
+using singleton_type = inery::singleton<"datatablele"_n, datatablele>;

4. Define The Singleton Instance

Define the singleton table instance as a data member of type singleton_type.

struct [[inery::table]] datatablele {
name primary_value;
uint64_t secondary_value;
};

using singleton_type = inery::singleton<"datatablele"_n, datatablele>;
+singleton_type singleton_instance;

5. Initialize And Use The Singleton Instance

Initialize the singleton_instance using the constructor with the parameters receiver and code (the last one in in this case is receiver.value). These parameters, combined with datatablele, provide access to the partition of the RAM cache used by this singleton. In our example you initialize the singleton_instance data member in the value contract constructor, see below:

// singleton contract constructor
singleton_example( name receiver, name code, datastream<const char*> ds ) :
contract(receiver, code, ds),
+ singleton_instance(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_instance data member. Below you can find a possible implementation for the full class singleton example contract.

singleton_example.hpp

#include <inery/inery.hpp>
#include <inery/singleton.hpp>
using namespace inery;

class [[inery::contract]] singleton_example : public contract {
public:
using contract::contract;
singleton_example( name receiver, name code, datastream<const char*> ds ) :
contract(receiver, code, ds),
singleton_instance(receiver, receiver.value)
{}

[[inery::action]]
void set( name user, uint64_t value );
[[inery::action]]
void get( );

struct [[inery::table]] datatablele {
name primary_value;
uint64_t secondary_value;
uint64_t primary_key() const { return primary_value.value; }
} datatablelerow;

using singleton_type = inery::singleton<"datatablele"_n, datatablele>;
singleton_type singleton_instance;

};

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.

singleton_example.cpp

#include <singleton_example.hpp>

[[inery::action]] void singleton_example::set( name user, uint64_t value ) {
auto entry_stored = singleton_instance.get_or_create(user, datatablelerow);
entry_stored.primary_value = user;
entry_stored.secondary_value = value;
singleton_instance.set(entry_stored, user);
}

[[inery::action]] void singleton_example::get( ) {
if (singleton_instance.exists())
inery::print(
"Value stored for: ",
name{singleton_instance.get().primary_value.value},
" is ",
singleton_instance.get().secondary_value,
"\n");
else
inery::print("Singleton is empty\n");
}

Summary

In conclusion, the above instructions show how to define a singleton.