Skip to content

ICU-23136 Remove __declspec(dllexport) from class Locale on Windows #3521

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

roubert
Copy link
Member

@roubert roubert commented Jun 10, 2025

Checklist

  • Required: Issue filed: ICU-23136
  • Required: The PR title must be prefixed with a JIRA Issue number. Example: "ICU-1234 Fix xyz"
  • Required: Each commit message must be prefixed with a JIRA Issue number. Example: "ICU-1234 Fix xyz"
  • Issue accepted (done by Technical Committee after discussion)
  • Tests included, if applicable
  • API docs and/or User Guide docs changed or added, if applicable

@roubert roubert closed this Jun 11, 2025
@roubert roubert deleted the debug branch June 11, 2025 12:48
@roubert roubert restored the debug branch June 11, 2025 12:49
@roubert roubert reopened this Jun 11, 2025
@roubert roubert changed the title ICU-20392 DEBUG Windows DLL [DO NOT MERGE] ICU-23136 Remove __declspec(dllexport) from class Locale on Windows Jun 13, 2025
@roubert
Copy link
Member Author

roubert commented Jun 13, 2025

There's a testtagsguards.sh failure in clang-release-build-and-test, what's the correct solution for that?

@roubert roubert marked this pull request as ready for review June 13, 2025 16:19
Comment on lines +1181 to +1183
struct Foo;
std::unique_ptr<Foo> foo;
std::variant<std::monostate, int, std::unique_ptr<Foo>> bar;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a leftover from experimenting. Please remove.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not a leftover, that's very intentionally there to verify that this all really works, just as the commit description says: "TEST: std::unique_ptr & std::variant members. [DO NOT MERGE]".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't looking one commit at a time. As long as you remember to remove this commit before merging it's ok.

@@ -369,7 +372,7 @@ class U_COMMON_API Locale : public UObject {
* @system
* @stable ICU 2.0
*/
static const Locale& U_EXPORT2 getDefault();
U_EXPORT static const Locale& getDefault();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are some methods U_EXPORT instead of U_COMMON_API?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know, but it looks to me as if the pattern was that static members were U_EXPORT2 (while non-static members just inherited U_…_API from the class). Should it not be like that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole song and dance with these macros has grown over a long period of time trying to make various platforms and compilers happy.

I assume that U_COMMON_API on a whole class also applies to static members, that's why I am surprised you are not adding it to them as well. I think we needed the additional U_EXPORT2 for some consistency in calling conventions.

I also don't know what effect it has that you are replacing U_EXPORT2 after the return type with U_EXPORT before the return type. These kinds of things are largely compiler-specific. I seem to remember that the mainframe compilers were especially picky, but we don't have them in CI. I guess we will see if anyone complains after ICU 78 that their platform is now broken, and then we ask them to come up with a pull request that fixes theirs and doesn't break others.

Why are you replacing U_EXPORT2 after the return type with U_EXPORT before the return type?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you replacing U_EXPORT2 after the return type with U_EXPORT before the return type?

That I do know! That's necessary to do, for otherwise MSVC will fail with a compilation error.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you replacing U_EXPORT2 after the return type with U_EXPORT before the return type?

That I do know! That's necessary to do, for otherwise MSVC will fail with a compilation error.

... but it has worked so far. Would it work with the U_EXPORT2 in its original place if the public static members also had U_COMMON_API like they (I assume) used to inherit from the enclosing class?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it work with the U_EXPORT2 in its original place if the public static members also had U_COMMON_API like they (I assume) used to inherit from the enclosing class?

I don't know.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it work with the U_EXPORT2 in its original place if the public static members also had U_COMMON_API like they (I assume) used to inherit from the enclosing class?

I don't know.

Please try this with one of the static functions and see if it passes CI.
I would be more comfortable if this was a more consistent move-U_COMMON_API-onto-every-public-member change.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please try this with one of the static functions and see if it passes CI.

I don't have any reasonably easy way to try that. These changes are already done through trial-and-error until I found something that happened to work. Trying out more things just to see if they might work too wouldn't be a meaningful use of time.

This makes private std::unique_ptr & std::variant members possible.
@jira-pull-request-webhook
Copy link

Notice: the branch changed across the force-push!

  • icu4c/source/common/unicode/utypes.h is different

View Diff Across Force-Push

~ Your Friendly Jira-GitHub PR Checker Bot

@markusicu markusicu self-assigned this Jun 17, 2025
@mihnita
Copy link
Contributor

mihnita commented Jun 18, 2025

Digging in the export tables, and a bunch of entries seems to be dropped (not exported anymore).

I was unable to figure out a clear rule.
I've seen both static an non static methods disappearing, public / protected / private.

Most worrisome (I think) is the disappearance of some public entries, mostly constructors.
And not only the default ones, generated by the compiler, but constructors with parameters, that I can find declared in the header file.

The intriguing part is that not all constructors disappear, only some.
Again, I can't see the rule.

@mihnita
Copy link
Contributor

mihnita commented Jun 18, 2025

Why are you replacing U_EXPORT2 after the return type with U_EXPORT before the return type?

That I do know! That's necessary to do, for otherwise MSVC will fail with a compilation error.

I think that since U_EXPORT ends up being __declspec(dllexport), this applies:

To export functions, the __declspec(dllexport) keyword must appear to the left of the calling-convention keyword, if a keyword is specified. For example:

__declspec(dllexport) void __cdecl Function1(void);

To export all of the public data members and member functions in a class, the keyword must appear to the left of the class name as follows:

class __declspec(dllexport) CExampleExport : public CObject
{ ... class definition ... };

https://learn.microsoft.com/en-us/cpp/build/exporting-from-a-dll-using-declspec-dllexport?view=msvc-170

So U_EXPORT must be on the left of the return type.


U_EXPORT2 maps to __cdecl

Place the __cdecl modifier before a variable or a function name.

https://learn.microsoft.com/en-us/cpp/cpp/cdecl?view=msvc-170

I think that calling it ?_EXPORT? is confusing, as these are not actually exported.
But hey, it is probably ancient history, not worth shaking the boat.

@markusicu
Copy link
Member

Digging in the export tables, and a bunch of entries seems to be dropped (not exported anymore).
[...]
I've seen both static an non static methods disappearing, public / protected / private.
Most worrisome (I think) is the disappearance of some public entries, mostly constructors.
And not only the default ones, generated by the compiler, but constructors with parameters, that I can find declared in the header file.

C++ likes to auto-generate member functions. We have tried to define them explicitly, so that we can tag them with their status, but I think that's becoming less tenable with it generating more and more things like symmetric operator== and opterator!=.

If the auto-generated ones are no longer reliably exported, then that seems like a problem.

No longer reliably exporting explicitly declared methods is of course even worse :-(

Why are you replacing U_EXPORT2 after the return type with U_EXPORT before the return type?

That I do know! That's necessary to do, for otherwise MSVC will fail with a compilation error.

I think that since U_EXPORT ends up being __declspec(dllexport), this applies:

I reminded myself that U_COMMON_API is the same as U_EXPORT or U_IMPORT or nothing depending on whether we compile ICU itself or user code.

So I think these static member functions should use U_COMMON_API instead of U_EXPORT.

And if it is harmless (= does not break CI) to also keep the U_EXPORT2 where it's always been, then so much the better.

IOW please try to treat the static member functions the same as the non-static ones.

@mihnita since you have a Windows machine, can you please try this?
For example:

U_COMMON_API static const Locale& U_EXPORT2 getDefault();

@mihnita
Copy link
Contributor

mihnita commented Jun 20, 2025

@mihnita since you have a Windows machine, can you please try this?
For example:
U_COMMON_API static const Locale& U_EXPORT2 getDefault();

There is no difference in the export table between this and what Fredrik does.

@mihnita
Copy link
Contributor

mihnita commented Jun 23, 2025

@mihnita since you have a Windows machine, can you please try this? For example:

I tried, there is no difference in the export table between this and the result with the current PR change.

And I am pretty sure I've commented this a few days ago, but somehow GitHub "forgot" it.
Or maybe my memory is playing tricks on me...

@markusicu
Copy link
Member

@mihnita since you have a Windows machine, can you please try this? For example:

I tried, there is no difference in the export table between this and the result with the current PR change.

Ok. So Windows/Mac/Linux don't care either way.

The current attribute combinations are a result of long-ago trial-and-error with a larger set of platforms. Is there any reason not to make the more conservative change? If not, then I am willing to add a commit to that effect.

@roubert
Copy link
Member Author

roubert commented Jul 1, 2025

Is there any reason not to make the more conservative change?

I would just like to avoid making any change at all before we find someone who can say what the preferred way of doing this really is. At the moment we have two potential options but we don't know whether any of them is the preferred way, there might be a third option that we're not yet aware of, so making any such change now would be wasted effort if it later needs to be changed again. That's all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants