3

This might sound basic but:

WORD wSong = 0;
CArchive ar;
...
ar >> wSong;
m_sPublicTalkInfo.iSongStart = static_cast<int>(wSong);

At the moment I read the WORD into a specific variable and the cast it.

Can I read it in and cast at the same time?

Please note I can't serialize a int. It must be a WORD and cast to int. Or

ar >> wSong;
m_sPublicTalkInfo.iSongStart = static_cast<int>(wSong);
Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164

2 Answers2

5

I don't think there is a direct way. You could implement a helper function:

template <typename T, typename U>
T readAndCast (CArchive& ar) {
  U x;
  ar >> x;
  return static_cast<T> (x);
}


m_sPublicTalkInfo.iSongStart = readAndCast<int, WORD>(ar);

It might be better to use the fixed-width integer types in your program, i.e. perhaps int_least16_t instead of int to be sure the type has the right size. WORD is fixed to 16bit, but int isn't. Also, WORD is unsigned and int isn't, so there could be an overflow during casting.

Erlkoenig
  • 2,664
  • 1
  • 9
  • 18
  • Thanks. My numbers are small 0 to 151. I might be able to adjust to use `int_least16_t`. Not used those types before. – Andrew Truckle Nov 07 '19 at 10:42
  • 1
    Well, then `uint_least8_t` would be appropriate. – Erlkoenig Nov 07 '19 at 10:50
  • I have never really used `template` either in my own code (apart from with maps and lists). But I see how this is useful because we can pass many pair combinations and have one function. :) Nice one. – Andrew Truckle Nov 07 '19 at 12:56
2

This is a example of how you could create a wrapper if you want the serialize syntax to remain consistent. It's designed to work with integrals and MFC unsigned types only.

#include <iostream>
#include <cstdint>
#include <sstream>
#include <type_traits>

// Fake the MFC types
using BYTE = std::uint8_t;
using WORD = std::uint16_t;
using DWORD = std::uint32_t;
using QWORD = std::uint64_t;

template<typename T>
struct is_valid_save_type : std::bool_constant<
    std::is_same_v<BYTE, T> ||
    std::is_same_v<WORD, T> ||
    std::is_same_v<DWORD, T> ||
    std::is_same_v<QWORD, T>
> {};

template<typename T>
struct is_valid_load_type : is_valid_save_type<T> {};

// Saves type T as a SaveType
template<typename T, typename SaveType>
struct save_as_type
{
    explicit save_as_type(T value) : value(value) {}

    explicit operator SaveType() const
    {
        return static_cast<SaveType>(value);
    }

private:
    T value;

    // This class only works with integrals
    static_assert(std::is_integral_v<T>);

    // SaveType should be BYTE/WORD/DWORD/QWORD only
    static_assert(is_valid_save_type<SaveType>::value);
};

// Loads type T as a LoadType
template<typename T, typename LoadType>
struct load_as_type
{
    explicit load_as_type(T& value) : value_(value) {}

    load_as_type& operator=(LoadType rhs)
    {
        value_ = rhs;
        return *this;
    }

private:
    T& value_;

    // T should be an integral
    static_assert(std::is_integral_v<T>);

    // T must be non-constant
    static_assert(!std::is_const_v<T>);

    // LoadType should be BYTE/WORD/DWORD/QWORD only
    static_assert(is_valid_load_type<LoadType>::value);
};

class CArchive;

// Make the above types serializable
template<typename T, typename SaveType>
CArchive& operator<<(CArchive& ar, save_as_type<T, SaveType> const& s)
{
    ar << static_cast<SaveType>(s);
}

template<typename T, typename LoadType>
CArchive& operator>>(CArchive& ar, load_as_type<T, LoadType> l)
{
    LoadType t{};
    ar >> t;
    l = t;
}

// Use the following two functions in your code
template<typename SaveType, typename T>
save_as_type<T, SaveType> save_as(T const& t)
{
    return save_as_type<T, SaveType>{ t };
}

template<typename LoadType, typename T>
load_as_type<T, LoadType> load_as(T& t)
{
    return load_as_type<T, LoadType>{ t };
}

// Prevent loading into temporaries; i.e. load_as<BYTE>(11);
template<typename T, typename LoadType>
load_as_type<T, LoadType> load_as(const T&& t) = delete;

// Fake MFC Archive
class CArchive
{
public:
    CArchive& operator<<(int i)
    {
        std::cout << "Saving " << i << " as an int\n";
        return *this;
    }

    CArchive& operator<<(BYTE b)
    {
        std::cout << "Saving " << (int)b << " as a BYTE\n";
        return *this;
    }

    CArchive& operator<<(WORD w)
    {
        std::cout << "Saving " << (int)w << " as a WORD\n";
        return *this;
    }

    CArchive& operator<<(DWORD d)
    {
        std::cout << "Saving " << (int)d << " as a DWORD\n";
        return *this;
    }

    CArchive& operator>>(int& i)
    {
        std::cout << "Loading as an int\n";
        return *this;
    }

    CArchive& operator>>(BYTE& b)
    {
        std::cout << "Loading as a BYTE\n";
        return *this;
    }

    CArchive& operator>>(WORD& w)
    {
        std::cout << "Loading as a WORD\n";
        return *this;
    }

    CArchive& operator>>(DWORD& d)
    {
        std::cout << "Loading as a DWORD\n";
        return *this;
    }
};

int main()
{
    CArchive ar;

    int out_1 = 1;
    int out_2 = 2;
    int out_3 = 3;
    int out_4 = 4;

    ar << out_1 <<
        save_as<BYTE>(out_2) <<
        save_as<WORD>(out_3) <<
        save_as<DWORD>(out_4);

    std::cout << "\n";

    int in_1 = 0;
    int in_2 = 0;
    int in_3 = 0;
    int in_4 = 0;

    ar >> in_1 >>
        load_as<BYTE>(in_2) >>
        load_as<WORD>(in_3) >>
        load_as<DWORD>(in_4);

    return 0;
}

Output:

Saving 1 as an int
Saving 2 as a BYTE
Saving 3 as a WORD
Saving 4 as a DWORD

Loading as an int
Loading as a BYTE
Loading as a WORD
Loading as a DWORD

Mercury Dime
  • 1,141
  • 2
  • 10
  • 10