Saving settings to Windows registry as a tuple Regarding an application written in C++ using the Windows API. I'd like to store a user setting like the window position on program exit, to the Windows registry and retrieve settings like these on the next start of the program.

I currently have 3 settings to be stored/retrieved to/from the registry.

typedef struct tagWINPOS
{
  DWORD dwWindowStyle;
  int iWindowX;
  int iWindowY;
} winpos_t;

HKEY hKeyApp = (HKEY)INVALID_HANDLE_VALUE;
const char szWindowStyle[] = "WindowStyle";
const char szWindowX[] = "WindowX";
const char szWindowY[] = "WindowY";


Later on in the code I'm calling functions like RegSetValueEx and RegQueryValueEx always three times. Like so:

int SaveSettings( HWND hWnd )
{
  winpos_t wpos;
  // window dimensions from hWnd are stored to wpos, not shown 

  RegSetValueEx( hKeyApp, szWindowStyle, 0,
    REG_DWORD,
    (const BYTE*)&amp;wpos.dwWindowStyle,
    sizeof(wpos.dwWindowStyle) );
  RegSetValueEx( hKeyApp, szWindowX, 0,
    REG_DWORD,
    (const BYTE*)&amp;wpos.iWindowX,
    sizeof(wpos.iWindowX) );
  RegSetValueEx( hKeyApp, szWindowY, 0,
    REG_DWORD,
    (const BYTE*)&amp;wpos.iWindowY,
    sizeof(wpos.iWindowY) );

  // further irrelevant code removed
}


Things start to look a bit tedious. So I was thinking to put all the parameters for one function call (say RegSetValueEx) into an std::array and then make a for loop iterating over all the array elements.

Because the Windows registry can store multiple data types, like DWORD (32-bit), QWORD (64-bit) and strings. I thought about std::variant to list all of these for this data field.

I'm started with a little test program like this:

#include &lt;cstddef&gt;
#include &lt;cstdio&gt;
#include &lt;cstdint&gt;
#include &lt;tuple&gt;
#include &lt;variant&gt;

typedef std::variant&lt; int32_t, int64_t, char* &gt; data_t;

typedef std::tuple&lt;
  data_t,       // setting data
  const char*,  // setting name
  std::size_t   // size of setting [bytes]
&gt; setting_t;

typedef std::array&lt; setting_t, 2 &gt; settings_t;

constexpr int reg_dword = 0;
constexpr int reg_qword = 1;

constexpr settings_t settings{
  std::make_tuple( (std::in_place_index&lt;reg_dword&gt;, 33), "Mydata", 4 ),
  std::make_tuple( (std::in_place_index&lt;reg_qword&gt;, 34), "Mydata2", sizeof(std::in_place_index_t&lt;reg_qword&gt;) ) };

int main()
{
  printf( "name: %s, value %ld\n", std::get&lt;1&gt;( settings[0] ), std::get&lt;0&gt;( std::get&lt;0&gt;( settings[0] ) ));

  getchar();
  return EXIT_SUCCESS;
}


Anyway, do people consider this approach a good one to get to my goal of having to spell out a single function call of RegSetValueEx inside a for loop?

The initialization of the array has much boilerplate as well as the effort to obtain values. What is a good approach to improve the readability of this? Maybe a constexpr function like std::make_tuple but then more specific for my case?
you need save winpos_t as single value with REG_BINARY instead 3 different values. possible implementation:

class CConfig
{
    HKEY _hKey;
public:

    ~CConfig()
    {
        if (_hKey)
        {
            RegCloseKey(_hKey);
        }
    }

    CConfig() : _hKey (0)
    {
    }

    LSTATUS Save(
        PCWSTR lpValueName,
        DWORD dwType,
        const void* lpData,
        DWORD cbData)
    {
        return RegSetValueExW(_hKey, lpValueName, 0, dwType, (BYTE*)lpData, cbData);
    }

    LSTATUS Load(
        PCWSTR lpValueName,
        PDWORD lpType,
        void* lpData,
        PDWORD lpcbData)
    {
        return RegQueryValueExW(_hKey, lpValueName, 0, lpType, (BYTE*)lpData, lpcbData);
    }

    LSTATUS Init(HKEY hKey, PCWSTR lpSubKey)
    {
        return RegCreateKeyExW(hKey, lpSubKey, 0, 0, 0, KEY_ALL_ACCESS, 0, &amp;_hKey, 0);
    }
};

void test_cfg()
{
    CConfig cfg;
    if (!cfg.Init(HKEY_CURRENT_USER, L"Software\\MyKey"))
    {
        struct winpos_t
        {
            DWORD dwWindowStyle;
            int iWindowX;
            int iWindowY;
        };

        winpos_t test_pos = { 1, 2, 3};

        static const PCWSTR szwinpos_t = L"winpos_t";

        if (!cfg.Save(szwinpos_t, REG_BINARY, &amp;test_pos, sizeof(test_pos)))
        {
            ULONG type, cb = sizeof(test_pos);
            RtlZeroMemory(&amp;test_pos, sizeof(test_pos));
            if (!cfg.Load(szwinpos_t, &amp;type, &amp;test_pos, &amp;cb) &amp;&amp; type == REG_BINARY &amp;&amp; cb == sizeof(test_pos))
            {
                DbgPrint("{%x, %x, %x}\n", test_pos.dwWindowStyle, test_pos.iWindowX, test_pos.iWindowY);
            }
        }
    }
}