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 6 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
@jira-pull-request-webhook

This comment was marked as outdated.

@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.

@markusicu
Copy link
Member

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.

Well, we don't seem to have an actual expert.
You are making changes, and it scares me a bit that they seem to do more than what we need.

Note also that the macros like U_COMMON_API resolve to either import or export depending on which code you are compiling. U_COMMON_API wants to export from the common library, and import into the i18n library, into ICU tools & tests, and into non-ICU code. Changing U_COMMON_API to just U_EXPORT seems misleading at best.

I will give it a try, see what CI says, and ask Mihai to try it on his Windows machine as well.

@mihnita
Copy link
Contributor

mihnita commented Jul 12, 2025

There is a pretty long list of differences between what is exported "the old way" and "the new way"

There are differences even for members declared public, which I would expect to be preserved.
And there are still present a lot of members that are private or protected.
Which I would expect to stop being exported.

Let's say we don't worry too much about extra exports (although I kind of think was the point of this exercise?)

But missing public exports seems worrisome.

See here an archive with the old / new exports: https://mihai-nita.net/tmp/diff_exports.zip


There is no risk that I built at different commit points.
Because I went to head, built / dump / cleanup, then applied the PR as a patch, then built / dump.
So the only differences between the 2 dumps was the PR.

@roubert
Copy link
Member Author

roubert commented Jul 14, 2025

There are differences even for members declared public, which I would expect to be preserved.

Apart from the ones that I believe to have been exported by mistake before, have you seen any other public declarations missing now? I can't find any such in your dump files (but I haven't checked every single entry that differs).

And there are still present a lot of members that are private or protected.

Are any of those such members that the changes in this PR ought to remove? The changes in this PR only touch a tiny fraction of the code base, most private or protected members should be unaffected by these changes.

@markusicu
Copy link
Member

There is a pretty long list of differences between what is exported "the old way" and "the new way"

Mihai and I are looking at the diffs. Some of the symbols that are no longer supported are private functions, and as long as they are not called from inline code in the header file, that's fine.

I worry a bit about no longer exporting some of the vtables, for example

const icu_78::Locale::`vftable'
const icu_78::DateFormatSymbols::`vftable'
const icu_78::DateIntervalFormat::`vftable'

and a bunch of others.
Are they not needed?

On the other hand, if CI checks are happy... 🤷 ?

Copy link
Member

@markusicu markusicu left a comment

Choose a reason for hiding this comment

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

Added another commit after Mihai and I reviewed the changes in symbol exports.

If this passes CI and no one has ideas for what else to test and what more to change, then I suggest we remove the first commit (the temporary test one), squash the rest, approve, and merge.

@markusicu
Copy link
Member

Added another commit after Mihai and I reviewed the changes in symbol exports.

If this passes CI

I broke CI. I should have known better: The three internal classes that I exported again have Locale fields, which must be why Fredrik un-exported them. I changed these to U_I18N_API_CLASS class now.

@markusicu
Copy link
Member

It looks like the MSVC builds, and thus CI checks overall, are happy now. So we are back to my suggestion:

If this passes CI and no one has ideas for what else to test and what more to change, then I suggest we remove the first commit (the temporary test one), squash the rest, approve, and merge.

@jira-pull-request-webhook
Copy link

Notice: the branch changed across the force-push!

  • icu4c/source/common/locid.cpp is no longer changed in the branch
  • icu4c/source/common/unicode/locid.h is different

View Diff Across Force-Push

~ Your Friendly Jira-GitHub PR Checker Bot

@markusicu
Copy link
Member

If this passes CI and no one has ideas for what else to test and what more to change, then I suggest we remove the first commit (the temporary test one), squash the rest, approve, and merge.

Elsewhere, @mihnita gave me a "thumb up" for this. I have now removed the test commit 540a96a so this should be ready for review & approval. Once approved, let's squash, and then merge.

@markusicu markusicu requested a review from mihnita July 18, 2025 21:43
Copy link
Contributor

@mihnita mihnita left a comment

Choose a reason for hiding this comment

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

Thank you very much for the patience

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
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy