Skip to content

Fix C++20 incompatibilities preventing compilation on MSVC (VS2017)#13

Closed
SirFerMoX wants to merge 1 commit intoLegionEmulationProject:legionfrom
SirFerMoX:legion
Closed

Fix C++20 incompatibilities preventing compilation on MSVC (VS2017)#13
SirFerMoX wants to merge 1 commit intoLegionEmulationProject:legionfrom
SirFerMoX:legion

Conversation

@SirFerMoX
Copy link

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.

  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

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.
@TheGhostGroup
Copy link

This project is aiming to be updated for building on vs2022 and not vs2017.

@SirFerMoX SirFerMoX closed this by deleting the head repository Mar 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants