Make Audacity build in C++17 ...

... Fixing many conditional operators with explicit construction of wxString
instead of simple string literals;

And fixing allocation of more strictly aligned structures on Mac, without need
for 10.14 as the minimum SDK, by means of some class-specific operators new
and delete.
This commit is contained in:
Paul Licameli 2021-02-02 11:44:00 -05:00 committed by Dmitry Vedenko
parent e6e81399f7
commit 77dab158c3
19 changed files with 112 additions and 32 deletions

View File

@ -116,7 +116,7 @@ set( CMAKE_PREFIX_PATH
#set( CMAKE_INTERPROCEDURAL_OPTIMIZATION_DEBUG OFF )
# Set the required C++ standard
set( CMAKE_CXX_STANDARD 14 )
set( CMAKE_CXX_STANDARD 17 )
set( CMAKE_CXX_STANDARD_REQUIRED ON )
# Use ccache if available

View File

@ -107,7 +107,8 @@ public:
if (Read(key, &str))
{
struct lconv *info = localeconv();
wxString dec = info ? wxString::FromUTF8(info->decimal_point) : wxT(".");
wxString dec =
info ? wxString::FromUTF8(info->decimal_point) : wxString(".");
str.Replace(wxT(","), dec);
str.Replace(wxT("."), dec);

View File

@ -1,5 +1,58 @@
/**********************************************************************
Audacity: A Digital Audio Editor
MemoryX.cpp
Paul Licameli
**********************************************************************/
#include "MemoryX.h"
// Make the symbol table non-empty
UTILITY_API void lib_utility_dummy_symbol()
{}
#ifdef __APPLE__
constexpr auto sizeof_align_val = sizeof(std::align_val_t);
void *NonInterferingBase::operator new(std::size_t count, std::align_val_t al)
{
using namespace std;
// Get an allocation with sufficient extra space to remember the alignment
// (And to do that, adjust the alignment to be not less than the alignment of
// an alignment value!).
// Also increase the allocation by one entire alignment.
al = max( al, static_cast<align_val_t>( alignof(align_val_t) ) );
const auto al_as_size = static_cast<size_t>(al);
auto ptr = static_cast<char*>(
::operator new( count + sizeof_align_val + al_as_size ) );
// Adjust the pointer to a properly aligned one, with a space just before it
// to remember the adjustment
ptr += sizeof_align_val;
auto integer = reinterpret_cast<uintptr_t>(ptr);
const auto partial = integer % al_as_size;
auto adjustment = partial ? al_as_size - partial : 0;
integer += adjustment;
ptr = reinterpret_cast<char*>(integer);
// Remember the adjustment
*(reinterpret_cast<size_t*>(ptr) - 1) = adjustment;
return ptr;
}
void NonInterferingBase::operator delete(void *ptr, std::align_val_t al)
{
// Find the adjustment
auto adjustment = *(reinterpret_cast<size_t*>(ptr) - 1);
// Apply the adjustment
auto p = reinterpret_cast<char*>(ptr) - adjustment - sizeof_align_val;
// Call through to default operator
::operator delete(p);
}
#endif

View File

@ -4,6 +4,7 @@
// C++ standard header <memory> with a few extensions
#include <iterator>
#include <memory>
#include <new> // align_val_t and hardware_destructive_interference_size
#include <cstdlib> // Needed for free.
#ifndef safenew
#define safenew new
@ -579,6 +580,36 @@ OutContainer transform_container( InContainer &inContainer, Function &&fn )
inContainer.begin(), inContainer.end(), fn );
}
//! Non-template helper for class template NonInterfering
/*!
If a structure contains any members with large alignment, this base class may also allow it to work in
macOS builds under current limitations of the C++17 standard implementation.
*/
struct UTILITY_API alignas(
#ifdef __WIN32__
std::hardware_destructive_interference_size
#else
// That constant isn't defined for the other builds yet
64 /* ? */
#endif
)
NonInterferingBase {
#ifdef __APPLE__
static void *operator new(std::size_t count, std::align_val_t al);
static void operator delete(void *ptr, std::align_val_t al);
#endif
};
/*! Given a structure type T, derive a structure with sufficient padding so that there is not false sharing of
cache lines between successive elements of an array of those structures.
*/
template< typename T > struct NonInterfering
: NonInterferingBase // Inherit operators; use empty base class optimization
, T
{
using T::T;
};
// These macros are used widely, so declared here.
#define QUANTIZED_TIME(time, rate) (floor(((double)(time) * (rate)) + 0.5) / (rate))
// dB - linear amplitude conversions

View File

@ -93,6 +93,6 @@ wxString ActiveProjects::Find(const FilePath &path)
gPrefs->SetPath(configPath);
return found ? key : wxT("");
return found ? key : wxString{};
}

View File

@ -541,7 +541,7 @@ constexpr size_t TimeQueueGrainSize = 2000;
#endif
struct AudioIoCallback::ScrubState
struct AudioIoCallback::ScrubState : NonInterferingBase
{
ScrubState(double t0,
double rate,

View File

@ -132,12 +132,11 @@ int audacityAudioCallback(
// Data must be default-constructible and either copyable or movable.
template<typename Data>
class MessageBuffer {
struct alignas( 64
//std::hardware_destructive_interference_size // C++17
) UpdateSlot {
struct UpdateSlot {
std::atomic<bool> mBusy{ false };
Data mData;
} mSlots[2];
};
NonInterfering<UpdateSlot> mSlots[2];
std::atomic<unsigned char> mLastWrittenSlot{ 0 };
@ -566,10 +565,11 @@ protected:
// These need not be updated atomically, because we rely on the atomics
// in the playback ring buffers to supply the synchronization. Still,
// align them to avoid false sharing.
alignas(64) struct Cursor {
struct Cursor {
size_t mIndex {};
size_t mRemainder {};
} mHead, mTail;
};
NonInterfering<Cursor> mHead, mTail;
void Producer(
const PlaybackSchedule &schedule, double rate, double scrubSpeed,

View File

@ -16,10 +16,10 @@ Paul Licameli split from AudioIO.h
#include <cfloat>
#include <functional>
#include <memory>
#include <vector>
#include <wx/string.h>
#include <wx/weakref.h> // member variable
#include "MemoryX.h"
struct PaDeviceInfo;
typedef void PaStream;
@ -117,6 +117,7 @@ struct AudioIOStartStreamOptions
///\brief A singleton object supporting queries of the state of any active
/// audio streams, and audio device capabilities
class AUDACITY_DLL_API AudioIOBase /* not final */
: public NonInterferingBase
{
public:
static AudioIOBase *Get();

View File

@ -49,7 +49,7 @@ END_EVENT_TABLE()
const wxSize gSize = wxSize(LYRICS_DEFAULT_WIDTH, LYRICS_DEFAULT_HEIGHT);
LyricsWindow::LyricsWindow(AudacityProject *parent)
: wxFrame( &GetProjectFrame( *parent ), -1, {},
: wxFrame( &GetProjectFrame( *parent ), -1, wxString{},
wxPoint(100, 300), gSize,
//v Bug in wxFRAME_FLOAT_ON_PARENT:
// If both the project frame and LyricsWindow are minimized and you restore LyricsWindow,

View File

@ -1408,7 +1408,7 @@ const wxSize kDefaultSize =
wxSize(MIXER_BOARD_MIN_WIDTH, MIXER_BOARD_MIN_HEIGHT);
MixerBoardFrame::MixerBoardFrame(AudacityProject* parent)
: wxFrame( &GetProjectFrame( *parent ), -1, {},
: wxFrame( &GetProjectFrame( *parent ), -1, wxString{},
wxDefaultPosition, kDefaultSize,
wxDEFAULT_FRAME_STYLE | wxFRAME_FLOAT_ON_PARENT)
, mProject(parent)

View File

@ -3032,7 +3032,7 @@ RegistryPath PluginManager::SettingsPath(const PluginID & ID, bool shared)
wxT("_") +
plug.GetVendor() +
wxT("_") +
(shared ? wxT("") : plug.GetSymbol().Internal());
(shared ? wxString{} : plug.GetSymbol().Internal());
return SETROOT +
ConvertID(id) +

View File

@ -1140,7 +1140,7 @@ FilePath ProjectFileIO::SafetyFileName(const FilePath &src)
int nn = 1;
auto numberString = [](int num) -> wxString {
return num == 1 ? "" : wxString::Format(".%d", num);
return num == 1 ? wxString{} : wxString::Format(".%d", num);
};
auto suffixes = AuxiliaryFileSuffixes();

View File

@ -14,7 +14,7 @@
#include "SampleFormat.h"
#include <atomic>
class RingBuffer {
class RingBuffer final : public NonInterferingBase {
public:
RingBuffer(sampleFormat format, size_t size);
~RingBuffer();
@ -43,16 +43,8 @@ class RingBuffer {
size_t Filled( size_t start, size_t end );
size_t Free( size_t start, size_t end );
enum : size_t { CacheLine = 64 };
/*
// We will do this in C++17 instead:
static constexpr size_t CacheLine =
std::hardware_destructive_interference_size;
*/
// Align the two atomics to avoid false sharing
alignas(CacheLine) std::atomic<size_t> mStart { 0 };
alignas(CacheLine) std::atomic<size_t> mEnd{ 0 };
NonInterfering< std::atomic<size_t> > mStart { 0 }, mEnd{ 0 };
const size_t mBufferSize;

View File

@ -66,7 +66,7 @@ void CommandMessageTarget::EndStruct(){
void CommandMessageTarget::AddItem(const wxString &value, const wxString &name){
wxString Padding;
Padding.Pad( mCounts.size() *2 -2);
Padding = (( value.length() < 15 ) || (mCounts.back()<=0)) ? "" : wxString("\n") + Padding;
Padding = (( value.length() < 15 ) || (mCounts.back()<=0)) ? wxString{} : wxString("\n") + Padding;
if( name.empty() )
Update( wxString::Format( "%s%s\"%s\"", (mCounts.back()>0)?", ":"", Padding, Escaped(value)));
else

View File

@ -242,7 +242,7 @@ TranslatableString NyquistEffect::GetDescription()
wxString NyquistEffect::ManualPage()
{
return mIsPrompt
? wxT("Nyquist_Prompt")
? wxString("Nyquist_Prompt")
: mManPage;
}
@ -2183,7 +2183,7 @@ bool NyquistEffect::Parse(
ctrl.label = tokens[4];
// valStr may or may not be a quoted string
ctrl.valStr = len > 5 ? tokens[5] : wxT("");
ctrl.valStr = len > 5 ? tokens[5] : wxString{};
ctrl.val = GetCtrlValue(ctrl.valStr);
if (ctrl.valStr.length() > 0 &&
(ctrl.valStr[0] == wxT('(') ||

View File

@ -244,7 +244,7 @@ void DeviceToolBar::UpdatePrefs()
int hostSelectionIndex = mHost->GetSelection();
wxString oldHost = hostSelectionIndex >= 0 ? mHost->GetString(hostSelectionIndex) :
wxT("");
wxString{};
auto hostName = AudioIOHost.Read();
// if the prefs host name doesn't match the one displayed, it changed

View File

@ -23,6 +23,8 @@
#include "FileConfig.h"
#include <cerrno> // for ENOENT
#if !defined(F_OK)
#define F_OK 0x00
#endif

View File

@ -115,7 +115,7 @@ void FileHistory::Load(wxConfigBase & config, const wxString & group)
{
mHistory.clear();
mGroup = group.empty()
? wxT("RecentFiles")
? wxString{ "RecentFiles" }
: group;
config.SetPath(mGroup);

View File

@ -57,7 +57,7 @@ wxChar NumberFormatter::GetDecimalSeparator()
{
#if wxUSE_INTL
struct lconv *info = localeconv();
wxString s = info ? wxString::FromUTF8(info->decimal_point) : wxT(".");
wxString s = info ? wxString::FromUTF8(info->decimal_point) : wxString(".");
if (s.empty())
{
// We really must have something for decimal separator, so fall
@ -75,7 +75,7 @@ bool NumberFormatter::GetThousandsSeparatorIfUsed(wxChar *sep)
{
#if wxUSE_INTL
struct lconv *info = localeconv();
wxString s = info ? wxString::FromUTF8(info->thousands_sep) : wxT("");
wxString s = info ? wxString::FromUTF8(info->thousands_sep) : wxString{};
if (s.empty())
{