Skip to content

Commit 8db1c06

Browse files
separated source (C++17) and testing (C++20) standards
This required editing the source to exclude designated initialisers which are not supported in C++17 except through compiler extensions. Such extensions are enabled by default in Clang and GCC but require explicit enabling in e.g. MSVC. We've here made the decision to widen default-compiler support by making struct initialisation a little uglier.
1 parent 49e5ff3 commit 8db1c06

File tree

11 files changed

+256
-224
lines changed

11 files changed

+256
-224
lines changed

CMakeLists.txt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,11 +278,18 @@ set_target_properties(QuEST PROPERTIES
278278
SOVERSION ${PROJECT_VERSION_MAJOR}
279279
)
280280

281-
# Add required C and C++ standards
281+
# Add required C and C++ standards.
282+
# Note the QuEST interface(s) require only C11 and C++14,
283+
# while the source code is entirely C++ and requires C++17,
284+
# and the tests further require C++20 (handled in tests/).
285+
# Yet, we here specify C++17 for the source, and C11 as only
286+
# applies to the C interface when users specify USER_SOURCE,
287+
# to attemptedly minimise user confusion. Users wishing to
288+
# link QuEST with C++14 should separate compilation.
282289
target_compile_features(QuEST
283290
PUBLIC
284291
c_std_11
285-
cxx_std_20
292+
cxx_std_17
286293
)
287294

288295
# Turn on all compiler warnings

quest/include/matrices.h

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
* definitions, for safety. Some intendedly private functions are necessarily
1010
* exposed here to the user, and are prefixed with an underscore.
1111
*
12+
* Note too that designated initialisers are generally avoided, since while
13+
* C99/C11 compatible, they are not explicitly supported by C++14 (only from
14+
* C++20) except through compiler extensions
15+
*
1216
* @author Tyson Jones
1317
* @author Richard Meister (aided in design)
1418
* @author Erich Essmann (aided in design, patched on MSVC)
@@ -321,13 +325,14 @@ extern void _validateNewElemsPtrNotNull(qcomp* ptr, const char* caller);
321325
static inline CompMatr1 getCompMatr1(qcomp** in) {
322326
_validateNewNestedElemsPtrNotNull(in, 1, __func__);
323327

324-
CompMatr1 out = {
325-
.numQubits = 1,
326-
.numRows = 2,
327-
.elems = {
328-
{in[0][0], in[0][1]},
329-
{in[1][0], in[1][1]}}
330-
};
328+
CompMatr1 out;
329+
330+
out.numQubits = 1;
331+
out.numRows = 2;
332+
for (int r=0; r<2; r++)
333+
for (int c=0; c<2; c++)
334+
out.elems[r][c] = in[r][c];
335+
331336
return out;
332337
}
333338

@@ -346,15 +351,14 @@ static inline CompMatr1 getCompMatr1(qcomp** in) {
346351
static inline CompMatr2 getCompMatr2(qcomp** in) {
347352
_validateNewNestedElemsPtrNotNull(in, 2, __func__);
348353

349-
CompMatr2 out = {
350-
.numQubits = 2,
351-
.numRows = 4,
352-
.elems = {
353-
{in[0][0], in[0][1], in[0][2], in[0][3]},
354-
{in[1][0], in[1][1], in[1][2], in[1][3]},
355-
{in[2][0], in[2][1], in[2][2], in[2][3]},
356-
{in[3][0], in[3][1], in[3][2], in[3][3]}}
357-
};
354+
CompMatr2 out;
355+
356+
out.numQubits = 2;
357+
out.numRows = 4;
358+
for (int r=0; r<4; r++)
359+
for (int c=0; c<4; c++)
360+
out.elems[r][c] = in[r][c];
361+
358362
return out;
359363
}
360364

@@ -374,11 +378,13 @@ static inline CompMatr2 getCompMatr2(qcomp** in) {
374378
static inline DiagMatr1 getDiagMatr1(qcomp* in) {
375379
_validateNewElemsPtrNotNull(in, __func__);
376380

377-
DiagMatr1 out = {
378-
.numQubits = 1,
379-
.numElems = 2,
380-
.elems = {in[0], in[1]}
381-
};
381+
DiagMatr1 out;
382+
383+
out.numQubits = 1;
384+
out.numElems = 2;
385+
for (int i=0; i<2; i++)
386+
out.elems[i] = in[i];
387+
382388
return out;
383389
}
384390

@@ -397,11 +403,13 @@ static inline DiagMatr1 getDiagMatr1(qcomp* in) {
397403
static inline DiagMatr2 getDiagMatr2(qcomp* in) {
398404
_validateNewElemsPtrNotNull(in, __func__);
399405

400-
DiagMatr2 out = {
401-
.numQubits = 2,
402-
.numElems = 4,
403-
.elems = {in[0], in[1], in[2], in[3]}
404-
};
406+
DiagMatr2 out;
407+
408+
out.numQubits = 2;
409+
out.numElems = 4;
410+
for (int i=0; i<4; i++)
411+
out.elems[i] = in[i];
412+
405413
return out;
406414
}
407415

quest/src/api/channels.cpp

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -131,26 +131,30 @@ SuperOp allocSuperOp(int numQubits) {
131131
qindex numRows = powerOf2(2 * numQubits);
132132
qindex numElems = numRows * numRows;
133133

134+
// attempt top allocate 1D memory
134135
qcomp* cpuMem = cpu_allocArray(numElems); // nullptr if failed
135136
qcomp* gpuMem = nullptr;
136137
if (getQuESTEnv().isGpuAccelerated)
137138
gpuMem = gpu_allocArray(numElems); // nullptr if failed
138139

139-
SuperOp out = {
140-
.numQubits = numQubits,
141-
.numRows = numRows,
140+
// prepare output SuperOp (avoiding C++20 designated initialiser)
141+
SuperOp out;
142+
out.numQubits = numQubits;
143+
out.numRows = numRows;
142144

143-
.cpuElems = cpu_allocAndInitMatrixWrapper(cpuMem, numRows), // nullptr if failed
144-
.cpuElemsFlat = cpuMem,
145-
.gpuElemsFlat = gpuMem,
145+
// attemptedly allocate 2D alias for 1D CPU memory
146+
out.cpuElems = cpu_allocAndInitMatrixWrapper(cpuMem, numRows); // nullptr if failed
147+
out.cpuElemsFlat = cpuMem;
148+
out.gpuElemsFlat = gpuMem;
146149

147-
.wasGpuSynced = cpu_allocHeapFlag() // nullptr if failed
148-
};
150+
// attemptedly allocate (un-initialised) flags in the heap so that struct copies are mutable
151+
out.wasGpuSynced = cpu_allocHeapFlag(); // nullptr if failed
149152

150153
// if heap flag allocated, mark it as unsynced (caller will handle if allocation failed)
151154
if (mem_isAllocated(out.wasGpuSynced))
152155
*(out.wasGpuSynced) = 0;
153156

157+
// caller will handle if any above allocations failed
154158
return out;
155159
}
156160

@@ -176,16 +180,14 @@ extern "C" KrausMap createKrausMap(int numQubits, int numOperators) {
176180
// validation ensures this never overflows
177181
qindex numRows = powerOf2(numQubits);
178182

179-
KrausMap out = {
180-
.numQubits = numQubits,
181-
.numMatrices = numOperators,
182-
.numRows = numRows,
183-
184-
.matrices = cpu_allocMatrixList(numRows, numOperators), // is or contains nullptr if failed
185-
.superop = allocSuperOp(numQubits), // heap fields are or contain nullptr if failed
186-
187-
.isApproxCPTP = util_allocEpsilonSensitiveHeapFlag(), // nullptr if failed
188-
};
183+
// attempt to allocate output KrausMap fields (avoiding C++20 designated initialiser)
184+
KrausMap out;
185+
out.numQubits = numQubits,
186+
out.numMatrices = numOperators,
187+
out.numRows = numRows,
188+
out.matrices = cpu_allocMatrixList(numRows, numOperators); // is or contains nullptr if failed
189+
out.superop = allocSuperOp(numQubits); // heap fields are or contain nullptr if failed
190+
out.isApproxCPTP = util_allocEpsilonSensitiveHeapFlag(); // nullptr if failed
189191

190192
// free memory before throwing validation error to avoid memory leaks
191193
freeAllMemoryIfAnyAllocsFailed(out); // sets out.matrices=nullptr if failed

quest/src/api/environment.cpp

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -130,21 +130,14 @@ void validateAndInitCustomQuESTEnv(int useDistrib, int useGpuAccel, int useMulti
130130
if (globalEnvPtr == nullptr)
131131
error_allocOfQuESTEnvFailed();
132132

133-
/// @todo the below memcpy is naughty (QuESTEnv has no trivial copy-assignment) and causes compiler warning. Fix!
134-
135-
// initialise it to a local env
136-
QuESTEnv env = {
137-
138-
// bind deployment info
139-
.isMultithreaded = useMultithread,
140-
.isGpuAccelerated = useGpuAccel,
141-
.isDistributed = useDistrib,
142-
143-
// set distributed info
144-
.rank = (useDistrib)? comm_getRank() : 0,
145-
.numNodes = (useDistrib)? comm_getNumNodes() : 1,
146-
};
147-
memcpy(globalEnvPtr, &env, sizeof(QuESTEnv));
133+
// bind deployment info to global instance
134+
globalEnvPtr->isMultithreaded = useMultithread;
135+
globalEnvPtr->isGpuAccelerated = useGpuAccel;
136+
globalEnvPtr->isDistributed = useDistrib;
137+
138+
// bind distributed info
139+
globalEnvPtr->rank = (useDistrib)? comm_getRank() : 0;
140+
globalEnvPtr->numNodes = (useDistrib)? comm_getNumNodes() : 1;
148141
}
149142

150143

quest/src/api/matrices.cpp

Lines changed: 47 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -216,26 +216,26 @@ extern "C" CompMatr createCompMatr(int numQubits) {
216216
qindex numRows = powerOf2(numQubits);
217217
qindex numElems = numRows * numRows;
218218

219+
// attempt to allocate 1D memory
219220
qcomp* cpuMem = cpu_allocArray(numElems); // nullptr if failed
220221
qcomp* gpuMem = nullptr;
221222
if (getQuESTEnv().isGpuAccelerated)
222223
gpuMem = gpu_allocArray(numElems); // nullptr if failed
223224

224-
// initialise all CompMatr fields inline because most are const
225-
CompMatr out = {
226-
.numQubits = numQubits,
227-
.numRows = numRows,
225+
// prepare output CompMatr (avoiding C++20 designated initialiser)
226+
CompMatr out;
227+
out.numQubits = numQubits;
228+
out.numRows = numRows;
228229

229-
// allocate flags in the heap so that struct copies are mutable
230-
.isApproxUnitary = util_allocEpsilonSensitiveHeapFlag(), // nullptr if failed
231-
.isApproxHermitian = util_allocEpsilonSensitiveHeapFlag(),
230+
// attemptedly allocate (un-initialised) flags in the heap so that struct copies are mutable
231+
out.isApproxUnitary = util_allocEpsilonSensitiveHeapFlag(); // nullptr if failed
232+
out.isApproxHermitian = util_allocEpsilonSensitiveHeapFlag();
233+
out.wasGpuSynced = cpu_allocHeapFlag(); // nullptr if failed
232234

233-
.wasGpuSynced = cpu_allocHeapFlag(), // nullptr if failed
234-
235-
.cpuElems = cpu_allocAndInitMatrixWrapper(cpuMem, numRows), // nullptr if failed
236-
.cpuElemsFlat = cpuMem,
237-
.gpuElemsFlat = gpuMem
238-
};
235+
// attemptedly allocate 2D alias for 1D CPU memory
236+
out.cpuElems = cpu_allocAndInitMatrixWrapper(cpuMem, numRows); // nullptr if failed
237+
out.cpuElemsFlat = cpuMem;
238+
out.gpuElemsFlat = gpuMem;
239239

240240
validateMatrixAllocs(out, __func__);
241241
setInitialHeapFlags(out);
@@ -250,24 +250,21 @@ extern "C" DiagMatr createDiagMatr(int numQubits) {
250250
// validation ensures this never overflows
251251
qindex numElems = powerOf2(numQubits);
252252

253-
// initialise all CompMatr fields inline because most are const
254-
DiagMatr out = {
255-
.numQubits = numQubits,
256-
.numElems = numElems,
257-
258-
// allocate flags in the heap so that struct copies are mutable
259-
.isApproxUnitary = util_allocEpsilonSensitiveHeapFlag(), // nullptr if failed
260-
.isApproxHermitian = util_allocEpsilonSensitiveHeapFlag(),
261-
.isApproxNonZero = util_allocEpsilonSensitiveHeapFlag(),
262-
.isStrictlyNonNegative = cpu_allocHeapFlag(), // nullptr if failed
263-
.wasGpuSynced = cpu_allocHeapFlag(),
253+
// prepare output DiagMatr (avoiding C++20 designated initialiser)
254+
DiagMatr out;
255+
out.numQubits = numQubits,
256+
out.numElems = numElems,
264257

265-
// 1D CPU memory
266-
.cpuElems = cpu_allocArray(numElems), // nullptr if failed
258+
// attempt to allocate (uninitialised) flags in the heap so that struct copies are mutable
259+
out.isApproxUnitary = util_allocEpsilonSensitiveHeapFlag(); // nullptr if failed
260+
out.isApproxHermitian = util_allocEpsilonSensitiveHeapFlag();
261+
out.isApproxNonZero = util_allocEpsilonSensitiveHeapFlag();
262+
out.isStrictlyNonNegative = cpu_allocHeapFlag(); // nullptr if failed
263+
out.wasGpuSynced = cpu_allocHeapFlag();
267264

268-
// 1D GPU memory
269-
.gpuElems = (getQuESTEnv().isGpuAccelerated)? gpu_allocArray(numElems) : nullptr // nullptr if failed or not needed
270-
};
265+
// attempt to allocate 1D memory (nullptr if failed or not allocated)
266+
out.cpuElems = cpu_allocArray(numElems);
267+
out.gpuElems = (getQuESTEnv().isGpuAccelerated)? gpu_allocArray(numElems) : nullptr;
271268

272269
validateMatrixAllocs(out, __func__);
273270
setInitialHeapFlags(out);
@@ -289,30 +286,27 @@ FullStateDiagMatr validateAndCreateCustomFullStateDiagMatr(int numQubits, int us
289286
qindex numElems = powerOf2(numQubits);
290287
qindex numElemsPerNode = numElems / (useDistrib? env.numNodes : 1); // divides evenly
291288

292-
FullStateDiagMatr out = {
293-
294-
.numQubits = numQubits,
295-
.numElems = numElems,
296-
297-
// data deployment configuration; disable distrib if deployed to 1 node
298-
.isGpuAccelerated = useGpuAccel,
299-
.isMultithreaded = useMultithread,
300-
.isDistributed = useDistrib && (env.numNodes > 1),
301-
.numElemsPerNode = numElemsPerNode,
302-
303-
// allocate flags in the heap so that struct copies are mutable
304-
.isApproxUnitary = util_allocEpsilonSensitiveHeapFlag(), // nullptr if failed
305-
.isApproxHermitian = util_allocEpsilonSensitiveHeapFlag(),
306-
.isApproxNonZero = util_allocEpsilonSensitiveHeapFlag(),
307-
.isStrictlyNonNegative = cpu_allocHeapFlag(), // nullptr if failed
308-
.wasGpuSynced = cpu_allocHeapFlag(),
309-
310-
// 1D CPU memory
311-
.cpuElems = cpu_allocArray(numElemsPerNode), // nullptr if failed
312-
313-
// 1D GPU memory
314-
.gpuElems = (useGpuAccel)? gpu_allocArray(numElemsPerNode) : nullptr, // nullptr if failed or not needed
315-
};
289+
// prepare output FullStateDiagMatr (avoiding C++20 designated initialiser)
290+
FullStateDiagMatr out;
291+
out.numQubits = numQubits;
292+
out.numElems = numElems;
293+
294+
// bind deployments, disabling distribution if using a single MPI node
295+
out.isGpuAccelerated = useGpuAccel;
296+
out.isMultithreaded = useMultithread;
297+
out.isDistributed = useDistrib && (env.numNodes > 1);
298+
out.numElemsPerNode = numElemsPerNode;
299+
300+
// allocate (unitialised) flags in the heap so that struct copies are mutable
301+
out.isApproxUnitary = util_allocEpsilonSensitiveHeapFlag(); // nullptr if failed
302+
out.isApproxHermitian = util_allocEpsilonSensitiveHeapFlag();
303+
out.isApproxNonZero = util_allocEpsilonSensitiveHeapFlag();
304+
out.isStrictlyNonNegative = cpu_allocHeapFlag(); // nullptr if failed
305+
out.wasGpuSynced = cpu_allocHeapFlag();
306+
307+
// allocate 1D memory (nullptr if failed or not allocated)
308+
out.cpuElems = cpu_allocArray(numElemsPerNode);
309+
out.gpuElems = (useGpuAccel)? gpu_allocArray(numElemsPerNode) : nullptr;
316310

317311
validateMatrixAllocs(out, __func__);
318312
setInitialHeapFlags(out);

0 commit comments

Comments
 (0)