Skip to content

Fix C++20 incompatibilities preventing compilation on Windows with MSVC (VS2017) #12

@SirFerMoX

Description

@SirFerMoX

Description:
The project currently fails to compile on Windows using Visual Studio 2017 (MSVC v141) due to multiple C++20 features being used in a codebase configured for C++17. Below is a detailed list of all the changes required to make the project compile successfully on Windows.

See Fork with fixes: #13

  1. dep/g3dlite/source/debugAssert.cpp — Line 106
    The assignment to LPTSTR from a string literal fails because the project uses Unicode character set.
// Before:
realLastErr = _T("Last error code does not exist.");

// After:
realLastErr = (LPTSTR)_T("Last error code does not exist.");
  1. src/common/Debugging/WheatyExceptionReport.cpp and WheatyExceptionReport.h
    The function GetExceptionString is declared and defined as returning LPTSTR but actually returns const char* literals, causing C2440 errors.
// Before (in both .cpp and .h):
LPTSTR GetExceptionString(DWORD dwCode)
LPTSTR WheatyExceptionReport::GetExceptionString(DWORD dwCode)

// After:
LPCTSTR GetExceptionString(DWORD dwCode)
LPCTSTR WheatyExceptionReport::GetExceptionString(DWORD dwCode)

Additionally, calls to DumpTypeIndex passing "" as the 7th argument need a cast:

// Before:
DumpTypeIndex(..., "");

// After:
DumpTypeIndex(..., (char*)"");
  1. src/common/Utilities/Types.h
    std::type_identity is a C++20 feature not available in C++17. A custom implementation needs to be added.
// Before:
struct find_type_if<Check, T1, Ts...> : std::conditional_t<Check<T1>::value, std::type_identity<T1>, find_type_if<Check, Ts...>>

// After — add custom type_identity before the struct:
template<typename T>
struct type_identity { using type = T; };

template<template<typename...> typename Check, typename T1, typename... Ts>
struct find_type_if<Check, T1, Ts...> : std::conditional_t<Check<T1>::value, type_identity<T1>, find_type_if<Check, Ts...>>
  1. src/server/database/Database/FieldValueConverters.h
    std::type_identity_t is C++20. Replace with the custom Trinity::type_identity.
// Before:
class PrimitiveResultValueConverter<char const*, std::type_identity_t>
using StringResultValueConverter = PrimitiveResultValueConverter<char const*, std::type_identity_t>;

// After:
class PrimitiveResultValueConverter<char const*, Trinity::type_identity>
using StringResultValueConverter = PrimitiveResultValueConverter<char const*, Trinity::type_identity>;
  1. src/server/database/Database/QueryCallback.cpp
    Template lambdas ([&](...)) are C++20. Replace with a struct Visitor, and the lambda passed as std::function<bool()>& must be stored in a named variable first.
// Before:
return std::visit([&]<typename Result>(std::future<Result>&& future)
{
    ...
}, std::move(_query));

// After:
struct Visitor
{
    QueryCallback& self;
    std::function<bool()>& checkStateAndReturnCompletion;

    bool operator()(std::future<QueryResult>&& future) { ... }
    bool operator()(std::future<PreparedQueryResult>&& future) { ... }
};
std::function<bool()> checkFn = checkStateAndReturnCompletion;
Visitor v{*this, checkFn};
return std::visit(v, std::move(_query));
  1. src/server/database/Database/MySQLPreparedStatement.cpp
    std::chrono::year_month_day, hh_mm_ss, and sys_days are C++20. Replace with C++17 compatible code using gmtime.
// Before:
std::chrono::year_month_day ymd(time_point_cast<std::chrono::days>(value));
std::chrono::hh_mm_ss hms(duration_cast<std::chrono::microseconds>(value - std::chrono::sys_days(ymd)));
MYSQL_TIME* time = reinterpret_cast<MYSQL_TIME*>(static_cast<char*>(param->buffer));
time->year = static_cast<int32>(ymd.year());
time->month = static_cast<uint32>(ymd.month());
time->day = static_cast<uint32>(ymd.day());
time->hour = hms.hours().count();
time->minute = hms.minutes().count();
time->second = hms.seconds().count();
time->second_part = hms.subseconds().count();

// After:
time_t tt = std::chrono::system_clock::to_time_t(value);
tm* t = gmtime(&tt);
MYSQL_TIME* time = reinterpret_cast<MYSQL_TIME*>(static_cast<char*>(param->buffer));
time->year = t->tm_year + 1900;
time->month = t->tm_mon + 1;
time->day = t->tm_mday;
time->hour = t->tm_hour;
time->minute = t->tm_min;
time->second = t->tm_sec;
time->second_part = 0;
  1. src/server/database/Database/QueryResult.cpp
    Same C++20 date functions (year, month, day, sys_days). Replace with std::tm and mktime.
// Before:
case MYSQL_TIMESTAMP_DATE:
    return sys_days(year(source.year) / month(source.month) / day(source.day));
case MYSQL_TIMESTAMP_DATETIME:
    return sys_days(year(source.year) / month(source.month) / day(source.day))
        + hours(source.hour) + minutes(source.minute)
        + seconds(source.second) + microseconds(source.second_part);

// After:
case MYSQL_TIMESTAMP_DATE:
{
    std::tm t = {};
    t.tm_year = source.year - 1900;
    t.tm_mon = source.month - 1;
    t.tm_mday = source.day;
    time_t tt = mktime(&t);
    return std::chrono::system_clock::from_time_t(tt);
}
case MYSQL_TIMESTAMP_DATETIME:
{
    std::tm t = {};
    t.tm_year = source.year - 1900;
    t.tm_mon = source.month - 1;
    t.tm_mday = source.day;
    t.tm_hour = source.hour;
    t.tm_min = source.minute;
    t.tm_sec = source.second;
    time_t tt = mktime(&t);
    return std::chrono::system_clock::from_time_t(tt)
        + std::chrono::microseconds(source.second_part);
}
  1. src/server/database/Database/MySQLConnection.cpp
    Designated initializers ({ .field = value }) are C++20. Replace with direct member assignment. Also, executor_work_guard has a deleted copy assignment operator, so it must be moved using aggregate initialization.
// Before:
m_workerThread = std::make_unique<WorkerThread>(WorkerThread{
    .ThreadHandle = std::thread([context] { context->run(); }),
    .WorkGuard = std::move(executorWorkGuard)
});

// After:
m_workerThread = std::make_unique<WorkerThread>(WorkerThread{
    std::thread([context] { context->run(); }),
    std::move(executorWorkGuard)
});
  1. src/server/game/Entities/Player/Player.cpp — Line 28732
    std::to_address is C++20. The field BuildingInfo.PacketInfo is a std::optional, so use has_value() and operator* instead.
// Before:
garrisonInfo.Buildings.push_back(std::to_address(plot->BuildingInfo.PacketInfo));

// After:
garrisonInfo.Buildings.push_back(plot->BuildingInfo.PacketInfo.has_value() ? &(*plot->BuildingInfo.PacketInfo) : nullptr);
  1. src/server/game/Handlers/PetHandler.cpp — Line 523
    Same std::to_address issue with std::optional.
// Before:
DeclinedName const* declinedname = std::to_address(packet.RenameData.DeclinedNames);

// After:
DeclinedName const* declinedname = packet.RenameData.DeclinedNames.has_value() ? &(*packet.RenameData.DeclinedNames) : nullptr;
  1. src/server/game/Spells/SpellEffects.cpp — Lines 4473 and 4476
    Same std::to_address issue with OptionalMovement::SpellEffectExtraData.
// Before (line 4473):
m_caster->GetMotionMaster()->MoveCharge(..., std::to_address(spellEffectExtraData));

// After:
m_caster->GetMotionMaster()->MoveCharge(..., spellEffectExtraData ? &(*spellEffectExtraData) : nullptr);
  1. src/server/scripts/Draenor/Highmaul/boss_the_butcher.cpp — Line 403
    std::random_shuffle was removed in C++17. Replace with std::shuffle and add #include .
// Before:
std::random_shuffle(l_Indexes.begin(), l_Indexes.end());

// After:
#include <random> // add at top of file
std::shuffle(l_Indexes.begin(), l_Indexes.end(), std::mt19937{std::random_device{}()});
  1. src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp — Line 105
    Same std::random_shuffle removal. Add #include .
// Before:
std::random_shuffle(group, group + 3);

// After:
#include <random> // add at top of file
std::shuffle(group, group + 3, std::mt19937{std::random_device{}()});

Environment:

  • OS: Windows 10/11
  • Compiler: MSVC v141 (Visual Studio 2017)
  • C++ Standard: /std:c++17 (though some code requires C++20 features)
  • Boost: 1.78.0
  • MySQL: 8.0
  • OpenSSL: 3.x

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions