Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Conditional compilation - struct contains member

61 views
Skip to first unread message

Frederick Gotham

unread,
Jul 18, 2020, 10:30:11 AM7/18/20
to
I have to write one source code file that will be common to three projects that are all different architectures (arm32, arm64, x86_64).

All 3 architectures have the same library, however the library in question is slightly altered for each architecture.

Note that I cannot alter the header files, nor can I use the size or modification date of the header file as an indicator of which architecture it is.

Library header file for 1st architecture:

struct Monkey {
int a;
};

Library header file for 2nd architecture:

struct Monkey {
int a, b;
};

Library header file for 3rd architecture:

struct Monkey {
int a, b, c;
};

And so then my single source file common to the three architectures would be something like:

#include "library_header.hpp"

int main(void)
{
Monkey obj;

obj.a = 1;

if constexpr ( contains_member(obj,b) )
obj.b = 2;

if constexpr ( contains_member(obj,c) )
obj.c = 3;
}

What is the cleanest way of doing this? Is there something in Boost for it?

Should I go with the first answer suggested by 'usta' here?:
https://stackoverflow.com/questions/12729827/c-conditional-compilation-based-on-member-presence




Alf P. Steinbach

unread,
Jul 18, 2020, 11:49:22 AM7/18/20
to
You've abstracted away too much.

But generally this looks like you have a problem X, you have come up
with a possible solution Y, it turns out that Y is difficult or
practically impossible to implement in a clean way (or at all), so you
ask about Y.

Ask about X instead.


- Alf


Frederick Gotham

unread,
Jul 18, 2020, 12:35:49 PM7/18/20
to
If you're saying that I should change the header file or that I should ask someone to change the header file, well I can't.

Öö Tiib

unread,
Jul 18, 2020, 12:57:06 PM7/18/20
to
I do not see that Alf suggested any solutions (like modifying
header file). Alf suggested to stop feeding us monkeys and tell
what really is different in interfaces of those implementations.
Your example of library I simply would not #include ever.

Barry Schwarz

unread,
Jul 18, 2020, 1:33:51 PM7/18/20
to
On Sat, 18 Jul 2020 07:29:59 -0700 (PDT), Frederick Gotham
<cauldwel...@gmail.com> wrote:

>I have to write one source code file that will be common to three projects that are all different architectures (arm32, arm64, x86_64).
>
>All 3 architectures have the same library, however the library in question is slightly altered for each architecture.
>
>Note that I cannot alter the header files, nor can I use the size or modification date of the header file as an indicator of which architecture it is.
>
>Library header file for 1st architecture:
>
> struct Monkey {
> int a;

#define my_Architecture_1

> };
>
>Library header file for 2nd architecture:
>
> struct Monkey {
> int a, b;

#define my_Architecture_2

> };
>
>Library header file for 3rd architecture:
>
> struct Monkey {
> int a, b, c;

#define my_Architecture_3

> };
>
>And so then my single source file common to the three architectures would be something like:
>
>#include "library_header.hpp"
>
>int main(void)
>{
> Monkey obj;
>
> obj.a = 1;
>
> if constexpr ( contains_member(obj,b) )

#if defined(my_Architecture_2)

> obj.b = 2;

#endif

>
> if constexpr ( contains_member(obj,c) )

#if defined(my_Architecture_3)

> obj.c = 3;

#endif

>}
>
>What is the cleanest way of doing this? Is there something in Boost for it?
>
>Should I go with the first answer suggested by 'usta' here?:
> https://stackoverflow.com/questions/12729827/c-conditional-compilation-based-on-member-presence
>
>
>

--
Remove del for email

Manfred

unread,
Jul 18, 2020, 2:15:12 PM7/18/20
to
On 7/18/2020 6:56 PM, Öö Tiib wrote:
> On Saturday, 18 July 2020 19:35:49 UTC+3, Frederick Gotham wrote:
>> On Saturday, July 18, 2020 at 5:49:22 PM UTC+2, Alf P. Steinbach wrote:
>>> On 18.07.2020 16:29, Frederick Gotham wrote:
>>>> I have to write one source code file that will be common to three projects that are all different architectures (arm32, arm64, x86_64).
<snip>
>>>
>>> You've abstracted away too much.
>>>
>>> But generally this looks like you have a problem X, you have come up
>>> with a possible solution Y, it turns out that Y is difficult or
>>> practically impossible to implement in a clean way (or at all), so you
>>> ask about Y.
>>>
>>> Ask about X instead.
>>>
>>>
>>> - Alf
>>
>>
>>
>> If you're saying that I should change the header file or that I should ask someone to change the header file, well I can't.
>
> I do not see that Alf suggested any solutions (like modifying
> header file). Alf suggested to stop feeding us monkeys and tell
> what really is different in interfaces of those implementations.
> Your example of library I simply would not #include ever.
>
>

The original question is about conditional compilation for different
architectures.
Platform specific details are usually kept very well confined, which is
why most often preprocessor conditionals fulfill the task, especially
considering that architecture specifics show up at the OS level, which
exposes C interfaces more often than not.

The suggestions seem to propagate architecture-specific details into
application interfaces, which is not a good idea, IMO.
Alf says that there has been too much abstraction - assuming this is for
application code, and not some OS component, I would say that the usual
abstraction that yields a platform independent design has simply not
been done in this case.

James Kuyper

unread,
Jul 18, 2020, 2:26:32 PM7/18/20
to
On 7/18/20 1:33 PM, Barry Schwarz wrote:
> On Sat, 18 Jul 2020 07:29:59 -0700 (PDT), Frederick Gotham
> <cauldwel...@gmail.com> wrote:
>
>> I have to write one source code file that will be common to three projects that are all different architectures (arm32, arm64, x86_64).
>>
>> All 3 architectures have the same library, however the library in question is slightly altered for each architecture.
>>
>> Note that I cannot alter the header files, nor can I use the size or modification date of the header file as an indicator of which architecture it is.
>>
>> Library header file for 1st architecture:
>>
>> struct Monkey {
>> int a;
>

Keeping in mind that the above declaration occurs inside a library
header file that he's not at liberty to modify, where exactly did you
intend for the following line to be inserted?

> #define my_Architecture_1

Daniel P

unread,
Jul 18, 2020, 4:16:22 PM7/18/20
to
On Saturday, July 18, 2020 at 10:30:11 AM UTC-4, Frederick Gotham wrote:
>
> Library header file for 1st architecture:
>
> struct Monkey {
> int a;
> };
>
> Library header file for 2nd architecture:
>
> struct Monkey {
> int a, b;
> };
>
> Library header file for 3rd architecture:
>
> struct Monkey {
> int a, b, c;
> };
>
> And so then my single source file common to the three architectures would be something like:
>
> #include "library_header.hpp"
>
> int main(void)
> {
> Monkey obj;
>
> obj.a = 1;
>
> if constexpr ( contains_member(obj,b) )
> obj.b = 2;
>
> if constexpr ( contains_member(obj,c) )
> obj.c = 3;
> }
>
> What is the cleanest way of doing this?
>
The cleanest way is to follow the C++ Detection Idiom introduced in
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4436.pdf.

If your compiler has std::experimental::is_detected_v defined in header
<experimental/type_traits>, your code is simply

#include <utility>
#include <experimental/type_traits>
#include <iostream>

struct Monkey {
int a, b;
};

template<class T>
using
struct_b_t = decltype(std::declval<T>().b);

template<class T>
using
struct_c_t = decltype(std::declval<T>().c);

int main()
{
std::cout << "(1) " << std::experimental::is_detected_v<struct_b_t, Monkey> << "\n";
std::cout << "(2) " << std::experimental::is_detected_v<struct_c_t, Monkey> << "\n";
}

Output:

(1) 1
(2) 0

But if your compiler doesn't have that, boost has boost::is_detected_v
defined in <boost/type_traits/is_detected.hpp>

Daniel

Manfred

unread,
Jul 18, 2020, 4:44:54 PM7/18/20
to
The documentation for the library should specify under which conditions
Monkey has which shape.
That is most likely some preprocessor definition that user code can use.
(I still have my doubts about architecture-specific features bubbling up
to application interfaces)

>
>> #define my_Architecture_1

Daniel P

unread,
Jul 18, 2020, 4:54:54 PM7/18/20
to
On Saturday, July 18, 2020 at 4:16:22 PM UTC-4, Daniel P wrote:
>
> If your compiler has std::experimental::is_detected_v defined in header
> <experimental/type_traits>, your code is simply
>
> #include <utility>
> #include <experimental/type_traits>
> #include <iostream>
>
> struct Monkey {
> int a, b;
> };
>
> template<class T>
> using
> struct_b_t = decltype(std::declval<T>().b);
>
> template<class T>
> using
> struct_c_t = decltype(std::declval<T>().c);
>
> int main()
> {
> std::cout << "(1) " << std::experimental::is_detected_v<struct_b_t, Monkey> << "\n";
> std::cout << "(2) " << std::experimental::is_detected_v<struct_c_t, Monkey> << "\n";
> }
>
> Output:
>
> (1) 1
> (2) 0
>
> But if your compiler doesn't have that, boost has boost::is_detected_v
> defined in <boost/type_traits/is_detected.hpp>
>

And to complete the example:

template <class T>
std::enable_if_t<is_detected<struct_b_t, T>::value>
f(T& val)
{
val.b = 2;
}

template <class T>
std::enable_if_t<!is_detected<struct_b_t, T>::value>
f(T&)
{
}

template <class T>
std::enable_if_t<is_detected<struct_c_t, T>::value>
g(T& val)
{
val.c = 3;
}

template <class T>
std::enable_if_t<!is_detected<struct_c_t, T>::value>
g(T&)
{
}

int main()
{
Monkey val;
f(val);
g(val);

std::cout << val.b << "\n";
}

Output:

2

James Kuyper

unread,
Jul 18, 2020, 5:13:02 PM7/18/20
to
On 7/18/20 4:44 PM, Manfred wrote:
> On 7/18/2020 8:26 PM, James Kuyper wrote:
>> On 7/18/20 1:33 PM, Barry Schwarz wrote:
>>> On Sat, 18 Jul 2020 07:29:59 -0700 (PDT), Frederick Gotham
>>> <cauldwel...@gmail.com> wrote:
>>>
>>>> I have to write one source code file that will be common to three projects that are all different architectures (arm32, arm64, x86_64).
>>>>
>>>> All 3 architectures have the same library, however the library in question is slightly altered for each architecture.
>>>>
>>>> Note that I cannot alter the header files, nor can I use the size or modification date of the header file as an indicator of which architecture it is.
>>>>
>>>> Library header file for 1st architecture:
>>>>
>>>> struct Monkey {
>>>> int a;
>>>
>>
>> Keeping in mind that the above declaration occurs inside a library
>> header file that he's not at liberty to modify, where exactly did you
>> intend for the following line to be inserted?
>
> The documentation for the library should specify under which conditions
> Monkey has which shape.
> That is most likely some preprocessor definition that user code can use.

That seems plausible - in which case a direct check of that macro is
what he should be doing. Ordinarily, I'd take it for granted that he
thought of that possibility, but didn't find anything suitable. But I'd
also expect him to mention the failed search, so he might not have
thought to check.

But checking for suitable macros that can be checked is what your
recommendation should be. Recommending

>>> #define my_Architecture_1

Without giving any suggestions about how to know whether or not it
should be #defined is pretty useless.

Barry Schwarz

unread,
Jul 18, 2020, 6:59:50 PM7/18/20
to
On Sat, 18 Jul 2020 07:29:59 -0700 (PDT), Frederick Gotham
<cauldwel...@gmail.com> wrote:

>I have to write one source code file that will be common to three projects that are all different architectures (arm32, arm64, x86_64).
>
>All 3 architectures have the same library, however the library in question is slightly altered for each architecture.
>
>Note that I cannot alter the header files, nor can I use the size or modification date of the header file as an indicator of which architecture it is.
>
>Library header file for 1st architecture:
>
> struct Monkey {
> int a;
> };
>
>Library header file for 2nd architecture:
>
> struct Monkey {
> int a, b;
> };
>
>Library header file for 3rd architecture:
>
> struct Monkey {
> int a, b, c;
> };
>
>And so then my single source file common to the three architectures would be something like:

Since each header file contains a struct Monkey, you can include only
one of the three header files in a given compilation.

Uncomment the appropriate #define below based on which header file you
want to include.

//For 1st architecture
//#define my_Architecture_1
//For 2nd architecture
//#define my_Architecture_2
//For 3rd architecture
//#define my_Architecture_3

>#include "library_header.hpp"

Replace with

#if defined(my_Architecture_1)
#include library_header_1.hpp
#elif defined(my_Architecture_2)
#include library_header_2.hpp
#elif defined(my_Architecture_3)
#include library+header_3.hpp
#endif

>int main(void)
>{
> Monkey obj;
>
> obj.a = 1;
>
> if constexpr ( contains_member(obj,b) )

Replace with
#if defined(my_Architecture_2) || defined(my_Architecture_3)

> obj.b = 2;

#endif

>
> if constexpr ( contains_member(obj,c) )

Replace with
#if defined(my_Architecture_3)

> obj.c = 3;

#endif

>}
>
>What is the cleanest way of doing this? Is there something in Boost for it?

Depends on how you measure cleanliness.

James Kuyper

unread,
Jul 18, 2020, 7:16:54 PM7/18/20
to
On 7/18/20 6:59 PM, Barry Schwarz wrote:
> On Sat, 18 Jul 2020 07:29:59 -0700 (PDT), Frederick Gotham
> <cauldwel...@gmail.com> wrote:
>
>> I have to write one source code file that will be common to three projects that are all different architectures (arm32, arm64, x86_64).
>>
>> All 3 architectures have the same library, however the library in question is slightly altered for each architecture.
>>
>> Note that I cannot alter the header files, nor can I use the size or modification date of the header file as an indicator of which architecture it is.
>>
>> Library header file for 1st architecture:
>>
>> struct Monkey {
>> int a;
>> };
>>
>> Library header file for 2nd architecture:
>>
>> struct Monkey {
>> int a, b;
>> };
>>
>> Library header file for 3rd architecture:
>>
>> struct Monkey {
>> int a, b, c;
>> };
>>
>> And so then my single source file common to the three architectures would be something like:
>
> Since each header file contains a struct Monkey, you can include only
> one of the three header files in a given compilation.

I'd gotten the impression from Gotham's message that the header file has
the same name regardless of which architecture is being used. If it
didn't have the same name, I see no reason why he would have mentioned
his inability to use the size or modification date of the header file to
determine which one it is. Using the file name would have been far
easier than either of those methods, if the filename were in fact different.
I've seen installation scripts that will install different versions of
the same file depending upon the target architecture, so that's one
plausible way for that to be the case.

Barry Schwarz

unread,
Jul 18, 2020, 9:05:15 PM7/18/20
to
In any individual compilation, there can be only one struct Monkey
type. Something has to change between one compilation and the next to
define a different struct Monkey type.

Given the OP's statement that here is a header file for each
architecture, he needs to tell us how he specifies which one to use.
If there is only one header file, he needs to tell us how he forces
only the desired type to be defined.

James Kuyper

unread,
Jul 18, 2020, 9:50:01 PM7/18/20
to
On 7/18/20 9:05 PM, Barry Schwarz wrote:
> On Sat, 18 Jul 2020 19:16:42 -0400, James Kuyper
> <james...@alumni.caltech.edu> wrote:
...
>> I'd gotten the impression from Gotham's message that the header file has
>> the same name regardless of which architecture is being used. If it
>> didn't have the same name, I see no reason why he would have mentioned
>> his inability to use the size or modification date of the header file to
>> determine which one it is. Using the file name would have been far
>> easier than either of those methods, if the filename were in fact different.
>> I've seen installation scripts that will install different versions of
>> the same file depending upon the target architecture, so that's one
>> plausible way for that to be the case.
>
> In any individual compilation, there can be only one struct Monkey
> type. Something has to change between one compilation and the next to
> define a different struct Monkey type.
>
> Given the OP's statement that here is a header file for each
> architecture, he needs to tell us how he specifies which one to use.

He's not seen fit to clarify the issue, so we can only guess. However,
I've seen multiple libraries whose installation scripts will install
one, and only one, header file of a given name, with the contents of
that file being different depending upon the architecture that the
library was installed on. If the library he's talking about shares that
feature, it's the installation script, not the OP, that specifies which
one to use - and he's looking for way to determine programmatically
which choice that was.

> If there is only one header file, he needs to tell us how he forces
> only the desired type to be defined.

If the library he's using is similar, in this regard, to the ones I'm
thinking of, the header file contains a declaration for only one
architecture-specific type. That is the desired type, and it was
determined when the library was installed.

However, for those libraries, doing what the OP wanted to do would have
been trivial. Their installation scripts used various methods to
determine which version of the library to install, and all of those
methods would be equally available to the OP. He would only need to read
the installation script and the installed code to find out how it made
that determination, and then do the same in his own code.
If I remember correctly (it's been a while), there were even macros
#defined by the headers that could be queried to determine which choice
the installation script had made, which would make the task even more
trivial.

Jorgen Grahn

unread,
Jul 19, 2020, 3:38:16 AM7/19/20
to
On Sat, 2020-07-18, Öö Tiib wrote:
> On Saturday, 18 July 2020 19:35:49 UTC+3, Frederick Gotham wrote:
>> On Saturday, July 18, 2020 at 5:49:22 PM UTC+2, Alf P. Steinbach wrote:
>> > On 18.07.2020 16:29, Frederick Gotham wrote:
>> > > I have to write one source code file that will be common to
>> > > three projects that are all different architectures (arm32,
>> > > arm64, x86_64).
>> > >
>> > > All 3 architectures have the same library, however the library
>> > > in question is slightly altered for each architecture.
>> > >
>> > > Note that I cannot alter the header files, nor can I use the
>> > > size or modification date of the header file as an indicator of
>> > > which architecture it is.
>> > >
>> > > Library header file for 1st architecture:
>> > >
>> > > struct Monkey {
>> > > int a;
>> > > };
>> > >
>> > > Library header file for 2nd architecture:
>> > >
>> > > struct Monkey {
>> > > int a, b;
>> > > };
...

>> > You've abstracted away too much.
>> >
>> > But generally this looks like you have a problem X, you have come up
>> > with a possible solution Y, it turns out that Y is difficult or
>> > practically impossible to implement in a clean way (or at all), so you
>> > ask about Y.
>> >
>> > Ask about X instead.
...

>> If you're saying that I should change the header file or that I
>> should ask someone to change the header file, well I can't.
>
> I do not see that Alf suggested any solutions (like modifying
> header file). Alf suggested to stop feeding us monkeys and tell
> what really is different in interfaces of those implementations.
> Your example of library I simply would not #include ever.

Put differently, it seems very odd to have a library where the public
interface changes in any relevant way depending on the CPU
architecture. Unless the library is from the 1980s, or something.

Libraries are supposed to /hide/ irrelevant differences, not
/amplify/ them.

We'd need to understand what's happening here, so we can avoid giving
bad advice.

Rereading the OP's text above, perhaps "library" and CPU
architecture were red herrings. Perhaps there are simply three forks
of a project which have begun to deviate, and the OP tries to
squeeze in "one source code file that will be common to [all] three
projects". But now I'm just speculating.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Kenny McCormack

unread,
Jul 19, 2020, 4:00:12 AM7/19/20
to
In article <rev5lp$1up$1...@dont-email.me>,
Alf P. Steinbach <alf.p.stein...@gmail.com> wrote:
...
>You've abstracted away too much.
>
>But generally this looks like you have a problem X, you have come up
>with a possible solution Y, it turns out that Y is difficult or
>practically impossible to implement in a clean way (or at all), so you
>ask about Y.
>
>Ask about X instead.

If you don't like this, then just mark it read and move on. No need to
post as you did.

--
"Unattended children will be given an espresso and a free kitten."

Kenny McCormack

unread,
Jul 19, 2020, 4:03:18 AM7/19/20
to
In article <69beaaae-fe19-42ff...@googlegroups.com>,
ร รถ Tiib <oot...@hot.ee> wrote:
...
>I do not see that Alf suggested any solutions (like modifying
>header file). Alf suggested to stop feeding us monkeys and tell
>what really is different in interfaces of those implementations.
>Your example of library I simply would not #include ever.

If you don't like it, just mark it read and move on.

--
"They say if you play a Microsoft CD backwards, you hear satanic messages.
Thats nothing, cause if you play it forwards, it installs Windows."

Öö Tiib

unread,
Jul 19, 2020, 5:52:17 AM7/19/20
to
On Sunday, 19 July 2020 11:03:18 UTC+3, Kenny McCormack wrote:
> In article <69beaaae-fe19-42ff...@googlegroups.com>,
> Öö Tiib <oot...@hot.ee> wrote:
> ...
> >I do not see that Alf suggested any solutions (like modifying
> >header file). Alf suggested to stop feeding us monkeys and tell
> >what really is different in interfaces of those implementations.
> >Your example of library I simply would not #include ever.
>
> If you don't like it, just mark it read and move on.

On the contrary, I did like it. Apparently you did like my
response as otherwise why did not you mark it read and move on?

Öö Tiib

unread,
Jul 19, 2020, 6:04:22 AM7/19/20
to
On Sunday, 19 July 2020 10:38:16 UTC+3, Jorgen Grahn wrote:
>
> We'd need to understand what's happening here, so we can avoid giving
> bad advice.

Yes, it is often case that people ask how to hack executable
to change data in it when the issue was about providing data
from command line.

> Rereading the OP's text above, perhaps "library" and CPU
> architecture were red herrings. Perhaps there are simply three forks
> of a project which have begun to deviate, and the OP tries to
> squeeze in "one source code file that will be common to [all] three
> projects". But now I'm just speculating.

The two common solutions to that are 1) to have side-by-side
wrappers for those different versions and 2) one heavily sliced
by preprocessor wrapper.

James Kuyper

unread,
Jul 19, 2020, 9:05:10 AM7/19/20
to
On 7/19/20 3:38 AM, Jorgen Grahn wrote:
...
> Put differently, it seems very odd to have a library where the public
> interface changes in any relevant way depending on the CPU
> architecture. Unless the library is from the 1980s, or something.

I think it's likely that the fields that are represented by "b" and "c"
in his simplified example are not intended to be part of the public
interface.

thomas.h...@gmail.com

unread,
Jul 20, 2020, 5:41:36 AM7/20/20
to
On Saturday, 18 July 2020 21:54:54 UTC+1, Daniel P wrote:

> > template<class T>
> > using
> > struct_b_t = decltype(std::declval<T>().b);
> >
> > template<class T>
> > using
> > struct_c_t = decltype(std::declval<T>().c);
>
> template <class T>
> std::enable_if_t<is_detected<struct_b_t, T>::value>
> f(T& val)
> {
> val.b = 2;
> }
>
> template <class T>
> std::enable_if_t<!is_detected<struct_b_t, T>::value>
> f(T&)
> {
> }


Thanks Daniel, I was able to get this to work with the C++11 compiler I'm using.

I was a little worried at first because my compiler doesn't support "if constexpr", but your method of having specialisations of template functions works.
0 new messages