Skip to content

(Objective-)C++ External linkage issues for global constants in PSSpecifier.h #30

Closed
@DGh0st

Description

@DGh0st

Importing the PSSpecifier.h in (Objective-)c++ context gives the following errors:

...
==> Compiling api/HSWidgetCombinedAdditionalOptionsAndPreferencesViewController.mm (arm64)…
In file included from api/HSWidgetCombinedAdditionalOptionsAndPreferencesViewController.mm:1:
In file included from api/HSWidgetCombinedAdditionalOptionsAndPreferencesViewController.h:3:
In file included from api/HSWidgetPreferencesListController.h:5:
/home/dgh0st/theos/vendor/include/Preferences/PSSpecifier.h:6:17: error: default initialization of an object of const type 'NSString *const'
NSString *const PSAccessoryKey; // @"accessory"
                ^
                               = 0
/home/dgh0st/theos/vendor/include/Preferences/PSSpecifier.h:7:17: error: default initialization of an object of const type 'NSString *const'
NSString *const PSActionKey; // @"action"
                ^
                            = 0
/home/dgh0st/theos/vendor/include/Preferences/PSSpecifier.h:8:17: error: default initialization of an object of const type 'NSString *const'
NSString *const PSAdjustFontSizeToFitWidthKey; // @"adjustFontSizeToFitWidth"
                ^
                                              = 0
/home/dgh0st/theos/vendor/include/Preferences/PSSpecifier.h:9:17: error: default initialization of an object of const type 'NSString *const'
NSString *const PSAlignmentKey; // @"alignment"
                ^
                               = 0
/home/dgh0st/theos/vendor/include/Preferences/PSSpecifier.h:10:17: error: default initialization of an object of const type 'NSString *const'
NSString *const PSAppGroupBundleIDKey; // @"appGroupBundleID"
                ^
                                      = 0
/home/dgh0st/theos/vendor/include/Preferences/PSSpecifier.h:11:17: error: default initialization of an object of const type 'NSString *const'
NSString *const PSAutoCapsKey; // @"autoCaps"
                ^
                              = 0
/home/dgh0st/theos/vendor/include/Preferences/PSSpecifier.h:12:17: error: default initialization of an object of const type 'NSString *const'
NSString *const PSAutoCorrectionKey; // @"autoCorrection"
                ^
                                    = 0
/home/dgh0st/theos/vendor/include/Preferences/PSSpecifier.h:13:17: error: default initialization of an object of const type 'NSString *const'
NSString *const PSBestGuesserKey; // @"bestGuess"
                ^
                                 = 0
/home/dgh0st/theos/vendor/include/Preferences/PSSpecifier.h:14:17: error: default initialization of an object of const type 'NSString *const'
NSString *const PSBundleCustomIconPathKey; // @"icon2"
                ^
                                          = 0
/home/dgh0st/theos/vendor/include/Preferences/PSSpecifier.h:15:17: error: default initialization of an object of const type 'NSString *const'
NSString *const PSBundleHasBundleIconKey; // @"hasBundleIcon"
                ^
                                         = 0
/home/dgh0st/theos/vendor/include/Preferences/PSSpecifier.h:16:17: error: default initialization of an object of const type 'NSString *const'
NSString *const PSBundleHasIconKey; // @"hasIcon"
                ^
                                   = 0
/home/dgh0st/theos/vendor/include/Preferences/PSSpecifier.h:17:17: error: default initialization of an object of const type 'NSString *const'
NSString *const PSBundleIconPathKey; // @"icon"
                ^
                                    = 0
/home/dgh0st/theos/vendor/include/Preferences/PSSpecifier.h:18:17: error: default initialization of an object of const type 'NSString *const'
NSString *const PSBundleIsControllerKey; // @"isController"
                ^
                                        = 0
/home/dgh0st/theos/vendor/include/Preferences/PSSpecifier.h:19:17: error: default initialization of an object of const type 'NSString *const'
NSString *const PSBundleOverridePrincipalClassKey; // @"overridePrincipalClass"
                ^
                                                  = 0
/home/dgh0st/theos/vendor/include/Preferences/PSSpecifier.h:20:17: error: default initialization of an object of const type 'NSString *const'
NSString *const PSBundlePathKey; // @"bundle"
                ^
                                = 0
/home/dgh0st/theos/vendor/include/Preferences/PSSpecifier.h:21:17: error: default initialization of an object of const type 'NSString *const'
NSString *const PSBundleTintedIconPathKey; // @"tintedIcon"
                ^
                                          = 0
/home/dgh0st/theos/vendor/include/Preferences/PSSpecifier.h:22:17: error: default initialization of an object of const type 'NSString *const'
NSString *const PSButtonActionKey; // @"buttonAction"
                ^
                                  = 0
/home/dgh0st/theos/vendor/include/Preferences/PSSpecifier.h:23:17: error: default initialization of an object of const type 'NSString *const'
NSString *const PSCancelKey; // @"cancel"
                ^
                            = 0
/home/dgh0st/theos/vendor/include/Preferences/PSSpecifier.h:24:17: error: default initialization of an object of const type 'NSString *const'
NSString *const PSCellClassKey; // @"cellClass"
                ^
                               = 0
fatal error: too many errors emitted, stopping now [-ferror-limit=]
20 errors generated.
make[3]: *** [/home/dgh0st/theos/makefiles/instance/rules.mk:201: /mnt/c/cygwin64/home/DGh0st/HSWidgets/.theos/obj/debug/arm64/api/HSWidgetCombinedAdditionalOptionsAndPreferencesViewController.mm.770ee366.o] Error 1
...

Notes:
For reference, the errors shown above are while compiling HSWidgets with latest Theos. But the issue is also reproduceable when creating a new empty preference subproject and including PSSpecifier.h in a Objective-c++ file (.mm).

Potential Root Cause:
I think the issue is the way these global constants are defined in PSSpecifier.h.

For C, following is the output after the pre-processor:

...
NSString *const PSAccessoryKey; // @"accessory"
NSString *const PSActionKey; // @"action"
NSString *const PSAdjustFontSizeToFitWidthKey; // @"adjustFontSizeToFitWidth"
...

By default C compiler provides external linkage for all non-static global variables so this works as expected.

For c++ global constants have internal linkage by default so we need the extern "C", which is what the __BEGIN_DECLS is responsible for. However c++ treats extern "C" blocks slightly differently when it comes to variables. According to the c++ standard, this is the difference that it has:

extern "C" int a; // declaration

extern "C" {
    int b; // definition
}

First is considered as declaration only, but second block statement is considered as a definition so the compiler needs to make sure memory will be created for b. If this were a const then it would require a default value. Which is the case for the PSSpecifier.h.

The way to make it not create space for the current translation unit, and ultimately not complain about the initialization, would be to either use:

extern "C" {
    extern int b; //  declaration
}

or

extern "C" int b; // declaration

(both of which are equivalent according to c++ standards)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions