0

I'm trying to create a struct with some constants in it like this:

#include <CoreAudio/CoreAudio.h>
...
struct properties {
    //Volume control
    const AudioObjectPropertyAddress volume = {
        kAudioDevicePropertyVolumeScalar, //mSelector
        kAudioDevicePropertyScopeOutput, //mScope
        0 //mElement
    };
    //Mute control
    const AudioObjectPropertyAddress mute = { 
        kAudioDevicePropertyMute,
        kAudioDevicePropertyScopeOutput,
        0
    };
};

However, I cannot access the constants in this class;

//Function used to for example set volume level or set mute status
//bool setProperty(UInt32 data_to_write, AudioObjectPropertyAddress addr_to_write_to);
//Following line should mute the system audio
setProperty(1, properties::mute);

This will make the compiler return the following error:

error: invalid use of non-static data member 'mute'

So, I tried making the constants static like this:

const static AudioObjectPropertyAddress volume = { ...

But now, I get a different error:

error: in-class initializer for static data member of type 'const AudioObjectPropertyAddress' requires 'constexpr' specifier

The last thing I tried is changing const static to static constexpr, however again, I cannot access the constants. Every time I try to access them, the compiler show this error:

Undefined symbols for architecture x86_64:
  "properties::mute", referenced from:
      _main in main-fefb9f.o
ld: symbol(s) not found for architecture x86_64

I'm not really sure what's going on here, I tried converting my struct into a class, but I ended up getting the same Undefined symbols error. I know I could just define the two constants as global variables, but I thought putting them into a struct/class would make the code look "nicer" or just more organized. Could someone please explain what's wrong here and provide a possible solution?

  • I believe that the requirement to give static struct/class members a placement outside of their enclosing struct/class definition came from legacy expectations where structs are inside of headers, thus even their definition could occur multiple times in multiple "compilation units". I expect this requirement to be removed eventually once C++20 modules become the main factor of C++ libraries/modularity. – rplgn Sep 27 '21 at 12:48
  • I don't see static in the first code block so it may have been missing when going to constexpr as well. Some classes may be incompatible with {} in the way you are using them- for example if they contain unions, you can only set some members. Provide what the members are or verify that your ={} works as intended in a simpler context. – Abel Sep 27 '21 at 13:11

3 Answers3

1

why not just properties.volume and properties.mute or use namespace otherwise...

namespace properties 
{
    //Volume control
    const AudioObjectPropertyAddress volume = {
        kAudioDevicePropertyVolumeScalar, //mSelector
        kAudioDevicePropertyScopeOutput, //mScope
        0 //mElement
    };

    //Mute control
    const AudioObjectPropertyAddress mute = { 
        kAudioDevicePropertyMute,
        kAudioDevicePropertyScopeOutput,
        0
    };
};
1
  •   struct properties {
          //Volume control
          const AudioObjectPropertyAddress volume = {
              kAudioDevicePropertyVolumeScalar, //mSelector
              kAudioDevicePropertyScopeOutput, //mScope
              0 //mElement
          };
      };
    

    is valid, but volume is not a static member, so usage requires an instance:

    setProperty(1, properties{}.volume);
    
  • With static const, it would be:

    struct properties {
        //Volume control
        static const AudioObjectPropertyAddress volume;
    };
    
    const properties::AudioObjectPropertyAddress volume {
        kAudioDevicePropertyVolumeScalar, //mSelector
        kAudioDevicePropertyScopeOutput, //mScope
        0 //mElement
    };
    

    and usage might be the expected:

    setProperty(1, properties::volume);
    
  • with static constexpr in C++11/C++14:

    struct properties {
        //Volume control
        static constexpr AudioObjectPropertyAddress volume{
            kAudioDevicePropertyVolumeScalar, //mSelector
            kAudioDevicePropertyScopeOutput, //mScope
            0 //mElement
        };
    };
    
    const properties::AudioObjectPropertyAddress volume; // Definition when ODR-used
    
  • with static /*inline*/ constexpr since C++17:

    struct properties {
        //Volume control
        static /*inline*/ constexpr AudioObjectPropertyAddress volume{
            kAudioDevicePropertyVolumeScalar, //mSelector
            kAudioDevicePropertyScopeOutput, //mScope
            0 //mElement
        };
    };
    
Jarod42
  • 203,559
  • 14
  • 181
  • 302
0

Here are a list of things you can try:

You could.... redesign your templating/types. Struct vs Namespace. Without seeing the whole project its tough to guide in a specific direction. Its up to you to decide if you need to use a struct, enum, namespace, class to encapsulate your code.

As for const static declaration. Here is a post describing valid methods of static class membder declarations. Pay close attention to your c++ version.

"Prior to C++11, only static const data members of integral or enumeration type could have initializers in the class definition."

Lastly, the static constexpr undefined. Is your static var in the same header/source file as where you are calling it? (Likely not, as the error shows the call from main). Static variables are not visible to other files.

If you want to modify static vars you could do so using a public get/set from a class function.

Suppose 'mute' is a single uint8_t value. Consider using a namespace with all static data declared as: static const uint8_t mute.

Frebreeze
  • 202
  • 3
  • 10