[RFC] FLTK 1.4 maintenance mode and 1.5 development

144 views
Skip to first unread message

Albrecht Schlosser

unread,
Feb 26, 2025, 12:28:57 PMFeb 26
to fltkc...@googlegroups.com
Dear FLTK developers,

after the release of FLTK 1.4.2  I believe it's a good time to begin FLTK 1.5 development. This means to switch FLTK 1.4 to "maintenance mode" and not to change it anymore except for severe bug fixes. As with branch-1.3 we can release further versions, particularly if necessary for Debian.

We did already decide that FLTK 1.4.x is the last release series for users that want/need to use autoconf/configure/make with all its issues, for instance lack of proper build dependency management. This double maintenance burden (configure + CMake) will be gone.

I discussed this already with Matthias and Manolo and we agreed that we want to begin 1.5 development as soon as possible.

I'd appreciate your comments to my/our proposals. Please let me know if I forgot any short-term items.

Notes:


- This vote is about the first steps only, further development will be discussed later.

- FLTK 1.5 shall be fully backwards compatible to 1.4's API as always (ABI will be changed). The goal is to make the switch from 1.4 to 1.5 as simple as possible for users (application developers): ideally a no-op, just recompile.

- Exceptions: Methods, functions, exported variables etc. that were already deprecated in FLTK 1.3.x shall be removed in 1.5.0 (stuff deprecated in 1.4 will be kept in 1.5).

- autoconf/configure/Makefiles will be removed. The only build option is CMake.

- Minimum required C++ standard (compiler) shall be C++17. This allows us to use C++17 features. We must not use C++ features of later standards.

- Minimum supported compiler versions? Must be capable of C++17, for instance VS 2019 or later.

- Minimum supported Windows and macOS versions? To be discussed separately.

- Users that can't follow these new requirements can use FLTK 1.4.x which will be updated with severe bug fixes for some time (to be defined).


Please vote for the proposals above so we can start development of FLTK 1.5 quickly.


As soon as this is decided I will create a new `branch-1.4` which will be the maintenance branch for FLTK 1.4.x (same as now `branch-1.3` for 1.3.x). I will also update the version number in branch `master` to 1.5.0. Developers can keep pushing to `master`.

Fl_String and Fl_Int_Vector classes will be removed and replaced with appropriate C++ classes.

The FL_OVERRIDE macro can be defined as `override` (unconditionally). It will be kept for backwards compatibility because user code may have used it. It should be deprecated and will be removed in a later FLTK version (maybe 1.6 or 1.7).

Some more technical changes will be made as appropriate.


Further thoughts and suggestions for 1.5 development
(to be discussed later):

- We may extend the public API with newer C++ features, for instance Fl_Widget::label(std::string) and/or Fl_Widget::label(std::string_view) but this needs thorough consideration to provide a good and useful API, not just "everything you can think of". The latter (std::string_view) requires C++17, for instance.

- We shall define a whitelist of features we allow to use in FLTK development. We should be conservative in our internal usage to keep the code readable and maintainable, i.e. don't use all shiny new C++17 features just because they exist.

- We can remove maintenance burdens like memory allocation and dealing with reallocations etc. by using modern C++ where appropriate. There's no need to change all instances of alloc/realloc/free though. This can be done step by step (or not at all).

- We should add new features as appropriate, for instance multi-touch features on touchscreens and touchpads.

- Dynamic loading of libraries (Windows DLL's) that didn't exist in earlier than the minimal Windows version (to be defined) can be removed and replaced with direct access. This can simplify the code and reduce potential bugs. We can use newer Windows API's which we didn't use because we wanted to support very old Windows versions.

- Similar thoughts apply to macOS: we can remove conditional code for "ancient" macOS versions once we decide which macOS versions we want to support. Maybe those released during the last 5 years?

That's it for today, please vote as written above. Thanks in advance and have a nice day.

melcher....@googlemail.com

unread,
Feb 26, 2025, 3:03:41 PMFeb 26
to fltk.coredev
Thank you, Albrecht, for publishing 1.4.2 and for pushing towards 1.5. I am very much looking forward to focusing on CMake and more modern C++, so it's a full=on thumbs up from me.

My plans are a thurough modernization of the Fluid source code, making it easier to maintain and expand. 

The second project is the reintroduction of iOS and Android support via SDL3. This will likely be implemented as an external driver so that the burden of maintaining SDL3 support is separate from the FLTK core.

Both of my plans are made a lot easier with the jump to C++17 and CMake-only. So again thumbs up from me!

Gonzalo Garramuño

unread,
Feb 27, 2025, 1:07:30 AMFeb 27
to fltkc...@googlegroups.com

On 2/26/2025 5:03 PM, 'melcher....@googlemail.com' via fltk.coredev wrote:
>
> The second project is the reintroduction of iOS and Android support
> via SDL3. This will likely be implemented as an external driver so
> that the burden of maintaining SDL3 support is separate from the FLTK
> core.
Will SDL3 support allow FLTK to use Wayland in some way.  To me that's
the biggest and only feature I am looking for 1.5+.

--
Gonzalo Garramuño
ggar...@gmail.com

Manolo

unread,
Feb 27, 2025, 1:56:25 AMFeb 27
to fltk.coredev
My vote is +1
Manolo

Gonzalo Garramuño

unread,
Feb 27, 2025, 2:48:31 AMFeb 27
to fltkc...@googlegroups.com

+1 from me!

--
You received this message because you are subscribed to the Google Groups "fltk.coredev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkcoredev...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/fltkcoredev/d19b575d-0b4e-4f63-82dd-dc1e8af0f328n%40googlegroups.com.
-- 
Gonzalo Garramuño
ggar...@gmail.com

Greg Ercolano

unread,
Mar 1, 2025, 1:57:28 PMMar 1
to fltkc...@googlegroups.com

On 2/26/25 09:28, 'Albrecht Schlosser' via fltk.coredev wrote:

I discussed this already with Matthias and Manolo and we agreed that we want to begin 1.5 development as soon as possible.

    Generally +1 with everything, but can't weigh in on C++17 adoption because
    I'm just not sure what it provides (I haven't been keeping up) that fltk internals
    absolutely need.

    My concern is that C++17 syntax is so different from what we've been supporting
    that it might make it very hard to back-port changes to 1.4. Potentially much harder
    than our to-date backports, where compiler syntax was never an issue.

    One reason I've not replied sooner is, quite simply:

        I'm not familiar with all the changes to C++  since the version we've been
        following to date (circa 1998?)

    There've been so many C++ versions since then; C++03/11/14/17/20..
    I've only vaguely followed some of the new features, sometimes having to try
    to read up on syntax I'm not familiar with when even novice C++ programmers
    post example code using these new syntax I'm not familiar with, e.g. lambdas
    and such.

    Certainly embracing std templates will help us a lot, so we can get rid of all the
    home brew stuff we do to allow dynamic arrays and such. That may be a lot of refitting.

    My own commercial code base is frozen in time with old C++ syntax, and use my own
    dynamic array/string classes to work around the absence of STL/std templates, and
    those custom classes are so deeply ingrained in my code, it would be hard to convert it
    to std, and for no real benefit to end users or myself. Even keeping my old API for those
    classes and retooling them to use std seems to be of no real benefit.

    I certainly use std for any new code I write, both personal/free source and one-off
    close source commercial projects I do for companies from time to time, using mostly
    only vector/string/streams/map/algorithm.


- FLTK 1.5 shall be fully backwards compatible to 1.4's API as always (ABI will be changed). The goal is to make the switch from 1.4 to 1.5 as simple as possible for users (application developers): ideally a no-op, just recompile.


    +1 Right sounds good.

- Exceptions: Methods, functions, exported variables etc. that were already deprecated in FLTK 1.3.x shall be removed in 1.5.0 (stuff deprecated in 1.4 will be kept in 1.5).

    +1 Yep, also sounds fine.



- autoconf/configure/Makefiles will be removed. The only build option is CMake.

    +1 Yay! Was never a fan of configure and m4 files.


- Minimum required C++ standard (compiler) shall be C++17. This allows us to use C++17 features. We must not use C++ features of later standards.

    Hmm, I abstain until I have further info, as I'm behind a bit.

    What is it specifically about c++17 specifically that we need it to be the minimum?
    I'm curious. A jump from C++98 all the way to c++17 seems a big leap.

    I would think all we /really/ need is std stuff, and that minimum could easily be had
    from earlier versions?

    I'm not really pushing back against c++17 adoption, just wondering why we need to
    jump so far through the standards.. for instance is C++11 not far enough?


    Regarding old platforms:
    I still have customers running on Windows7 and older Mac versions (Lion and up)
    but I don't /think/ customers have had trouble running my VS2019 generated
    executables on Win7.

    Not sure about mac C++17 compilers generating code that can run on older mac platforms.
    Ditto for linux. Linux is /especially/ touchy; I often have to compile on the same platforms
    the executable runs, due to all the damn C library interface issues.

    I don't think I've used my old "store bought" VS7 compiler for many years, and have been
    sticking with the free VS express versions (or whatever they're called now, maybe just VS).
    Windows has always been pretty good at backwards support for executables, linux the worst/hardest.



- Minimum supported compiler versions? Must be capable of C++17, for instance VS 2019 or later.
- Minimum supported Windows and macOS versions? To be discussed separately.

    For me Win7 and OSX Lion being minimum for my needs.
    I could probably freeze my code base on FLTK 1.4.x if 1.5 becomes a problem.




As soon as this is decided I will create a new `branch-1.4` which will be the maintenance branch for FLTK 1.4.x (same as now `branch-1.3` for 1.3.x). I will also update the version number in branch `master` to 1.5.0. Developers can keep pushing to `master`.

    ..where "master" will be 1.5.x I take it.



Fl_String and Fl_Int_Vector classes will be removed and replaced with appropriate C++ classes.

    +1 I guess


- We may extend the public API with newer C++ features, for instance Fl_Widget::label(std::string) and/or Fl_Widget::label(std::string_view) but this needs thorough consideration to provide a good and useful API, not just "everything you can think of". The latter (std::string_view) requires C++17, for instance.

    Ya, we probably should be careful with this.

    Embracing new c++ features in the public API may sound good on paper,
    but seems scary to me, in that the APIs would potentially become larger
    and redundant. (The old C way vs the new std/c++ way)

    Embracing also smells like fltk 2.x code fork territory, and raises concerns for me;
    being too big a thing to do in fltk, at least this soon in adoption.

    IMHO we should first bring the newer C++ into the internals, and get used to them
    and go thru many release cycles before we consider exposing them in the API,
    and all the subsequent impact that would have on docs and code examples,
    potentially doubling them..!

    History shows when we make sweeping improvements, we sometimes forget
    our roots/strengths/limitations, resulting in code fork fiascos.

    IMHO it's an important core tenet of fltk to not try to compete with other gui
    toolkits (like qt) which easily toss away backwards support in favor of full speed
    ahead with progress and new tech, abandoning old platforms and old app code.
    We're one of the few that don't, and I see that as a rare strength (perhaps our
    only strength lol)



- We shall define a whitelist of features we allow to use in FLTK development. We should be conservative in our internal usage to keep the code readable and maintainable, i.e. don't use all shiny new C++17 features just because they exist.

    +1 Right, both whitelist (things to use, e.g. std) and blacklist (things to never use, e.g. exceptions? boost, etc)


- We can remove maintenance burdens like memory allocation and dealing with reallocations etc. by using modern C++ where appropriate. There's no need to change all instances of alloc/realloc/free though. This can be done step by step (or not at all).

    +1


- We should add new features as appropriate, for instance multi-touch features on touchscreens and touchpads.

    Yes, that seems to be something we need to do, for myself not so much touchscreens/pads but for drawing tablets (pen oriented mouse movement with pressure sensitivity and pen orientation) which is important for paint and animation related applications, which I'm particularly interested in.


- Dynamic loading of libraries (Windows DLL's) that didn't exist in earlier than the minimal Windows version (to be defined) can be removed and replaced with direct access. This can simplify the code and reduce potential bugs. We can use newer Windows API's which we didn't use because we wanted to support very old Windows versions.

- Similar thoughts apply to macOS: we can remove conditional code for "ancient" macOS versions once we decide which macOS versions we want to support. Maybe those released during the last 5 years?

That's it for today, please vote as written above. Thanks in advance and have a nice day.


    Ya, later discussion on all that I think; I have no immediate response to that other than sounds good, but maybe longer than 5 years? 2017 maybe?

melcher....@googlemail.com

unread,
Mar 1, 2025, 3:36:30 PMMar 1
to fltk.coredev
er...@seriss.com schrieb am Samstag, 1. März 2025 um 19:57:28 UTC+1:

    Generally +1 with everything, but can't weigh in on C++17 adoption because

    I'm just not sure what it provides (I haven't been keeping up) that fltk internals
    absolutely need.

    My concern is that C++17 syntax is so different from what we've been supporting
    that it might make it very hard to back-port changes to 1.4. Potentially much harder
    than our to-date backports, where compiler syntax was never an issue.

There are several lists out there that help me choose a C++ version depending on my project goals. https://github.com/AnthonyCalandra/modern-cpp-features , for example.

C++11 is major, forget C++14, C++17 has a few nifty things and deals a tiny bit better with UTF8. As for big jumps, there is also C++20, 23, and 26 for even bigger jumps ;-)

C++11 is IMHO the absolute minimum that we should support and that we can expect from every user. `nullptr` to me is an absolute *must use* as it prevents a ton of errors and makes code much more readable. FLTK uses kludges that are now available like delegating constructors (one constructor can call another constructor). Non-static data member initializers, where you initialize a class variable in the header file already, is so much safer because you never have bugs reading uninitialized values. Smart pointers can  be helpful to avoid lost memory. It's too late for enum classes in the API though.

There are bunch of C++17 features I use regularly: `constexpr` is a lot smarter now,  nested namespaces help with brevity, `__has_include` replaces some cmake fiddling if it is very local, `std::filesystem` pretty much replaces our handwoven fl_filename stuff,  [[fallthrough]] avoids bugs, returning tuples is neat for dialogs for example.

So for a dialog box, you could write:

auto [action, text] = fl_input("Password");
`action` returns if the user canceled or pressed OK or close the window (could be an enum or int), so we don't have to do the "if the text is NULL..." kludge.

But then again, I am seeing this more from the Fluid perspective. For the FLTK insides, using C++11 where it makes things easier or safer is already a good step in the right direction. The last thing I want is recursive template trickery for no good reason.

 - Matthias

melcher....@googlemail.com

unread,
Mar 1, 2025, 3:38:08 PMMar 1
to fltk.coredev
PS: the official link for version changes: https://en.cppreference.com/w/cpp/17 , part of https://en.cppreference.com/w/

imm

unread,
Mar 1, 2025, 4:38:42 PMMar 1
to coredev fltk
TBH, I don't think I really mind about most of this... Except maybe exposing template stuff in the public API (fine to use it internally, just not in the API.)

Why? 
Because it isn't implemented uniformly across compiler versions, and so there's always a risk of weird compatibility issues.
MS used to caution against using it in APIs at all (and AFAIK they still do) because msvc doesn't even implement it in a compatible way between versions.

As for minimum C++ versions, again I don't really care. C++11 was good, but beyond that a lot of it's just been (usually in the name of "safer coding") putting lipstick on a particularly ugly pig...
(Yes, "We" need a safer language, C++ isn't it and never will be unless they get rid of all the old ways of doing things; simply adding better ways but leaving the "bad" way just makes the language bigger, not safer. And if they get rid of the old ways, fltk breaks, along with practically everything else...)
I don't much care for Rust either. I'm getting grumpy in my old age.

--
Ian
From my Fairphone FP3

Manolo

unread,
Mar 2, 2025, 2:07:03 AMMar 2
to fltk.coredev
Like Greg, I'm frightened by the perspective of choosing C++17 as minimum required
language standard for FLTK 1.5. That may impact the possibility to run FLTK
on ancient OS versions. That would introduce changes with, in my view, more adverse
effects than benefits. An example is "auto" that obscures the type of things whose type
used to be conspicuous in older C++.

Ian's point about not exposing templates in the public API is very important.

Albrecht Schlosser

unread,
Mar 2, 2025, 9:43:05 AMMar 2
to fltkc...@googlegroups.com
On 3/1/25 22:38 imm wrote:
TBH, I don't think I really mind about most of this... Except maybe exposing template stuff in the public API (fine to use it internally, just not in the API.)

Why? 
Because it isn't implemented uniformly across compiler versions, and so there's always a risk of weird compatibility issues.
MS used to caution against using it in APIs at all (and AFAIK they still do) because msvc doesn't even implement it in a compatible way between versions.

Ian, does this "template stuff" include std::string? Does this mean that we should NOT use std::string in our future API?

I talked with Matthias about some extensions of the API and we both believed that some overloads of existing methods would be helpful and expected by users.

(1) Extending existing API's for common usage, for instance:

Fl_Widget::copy_label(std::string S) {
  copy_label(S.c_str());
}

This would simplify user code that doesn't need to use c_str() if they use std::string() in their code. Sure, that's not really necessary but probably expected by users.

(2) There are some methods where we could return a std::string() object, for instance from "common dialogs" that return strings. In the past I introduced Fl_String for this reason but it was removed because we intended to use std::string in the next (1.5) FLTK version.

(3) Some methods that use an array of integer values as input arguments (setting multiple column widths or weights etc.) could use std::vector<int> instead (as an additional overload). This would simplify the API (no extra null (zero) value, implicit counter of values == array size). Users could perhaps even use '{ 10, 20, 30 }' which would be converted implicitly to a vector or something like that (not sure about that).

Back to the question(s):

- Should we avoid using std::string and std::vector in such IMHO useful corner cases because "it isn't implemented uniformly across compiler versions"? Are these "weird compatibility issues" really still the case in today's compilers, including VS2019 and later)? I can't tell. Anybody?

- What do other (C++) libraries do? Do they use for instance std::string and std::vector in their API's?

Please don't get me wrong: I'm not voting for lots of new methods with std::string etc. but I believe that some API's would benefit from such additional overloads. After all it would be a real mess if language features that were standardized almost 15 years ago couldn't be used in a public API.

Albrecht Schlosser

unread,
Mar 2, 2025, 10:29:50 AMMar 2
to fltkc...@googlegroups.com
Thanks to all for your comments and opinions.

After reading all comments thoroughly I believe that we have consensus on the following:

- We can start FLTK 1.5.0 development in branch `master`.
- FLTK 1.4.x will be in maintenance mode from now on in its own branch `branch-1.4`.
- We allow selected features of C++11 in the library itself (details to be decided).
- There must be a whitelist of C++11 features we want to use in the library (similar to the CMP used today).
- For a start the whitelist consists of std::string and std::vector, together with associated iterators.
- autoconf/configure/Makefiles will be removed (decided earlier).
- Backwards compatibility to supported platforms (Windows, macOS, Linux/X11) will not be changed deliberately.
- Build tools need to support at least C++11.
- Methods etc. deprecated in 1.3.x or earlier will be removed.

With these minimal decisions we can start FLTK 1.5 development.


I will create the new branch (`branch-1.4`) and will make the basic changes as described above.


To be discussed and decided later:

- API extensions with std::string and std::vector.
- Internal usage (aka whitelist) of more C++11 features like `auto`, `constexpr`, etc. (stay "conservative", don't allow "everything"). I strongly suggest to use `auto` in cases where sensible and unambiguous, and `constexpr` can really be an advantage w/o costs.
- Maybe usage of C++17 features which would make C++17 the minimal C++ standard for build systems but this can IMHO be postponed as suggested by some devs.

There is no reason to change lots of working code, but changes should be done where sensible, for instance if the code is worked on anyway or if it is error-prone for future maintenance. Manual allocation of arrays with hand-made classes should be changed as soon as possible so they can be tested in 1.5 (git) thoroughly before the release, e.g. in Fl_Help_View, maybe in Fl_Tree, and also in Fl_Table (not sure). (Fl_Int_Vector, class CharVector in Fl_Table_Row.H).

One example of this old and error-prone stuff is the array of children in Fl_Group which should be a vector (my working branch with this is ready to be merged).

IMHO our goal to release 1.5.0 should be in this year (2025).



On 2/26/25 18:28 'Albrecht Schlosser' wrote:
Dear FLTK developers,

after the release of FLTK 1.4.2  I believe it's a good time to begin FLTK 1.5 development. This means to switch FLTK 1.4 to "maintenance mode" and not to change it anymore except for severe bug fixes. As with branch-1.3 we can release further versions, particularly if necessary for Debian.

We did already decide that FLTK 1.4.x is the last release series for users that want/need to use autoconf/configure/make with all its issues, for instance lack of proper build dependency management. This double maintenance burden (configure + CMake) will be gone.

I discussed this already with Matthias and Manolo and we agreed that we want to begin 1.5 development as soon as possible.

I'd appreciate your comments to my/our proposals. Please let me know if I forgot any short-term items.

Notes:


- This vote is about the first steps only, further development will be discussed later.
...

melcher....@googlemail.com

unread,
Mar 2, 2025, 11:07:33 AMMar 2
to fltk.coredev
Albrecht-S schrieb am Sonntag, 2. März 2025 um 16:29:50 UTC+1:
- We can start FLTK 1.5.0 development in branch `master`. 
[etc. ]
Yes, thank you!
 
To be discussed and decided later:
Yes, thank you for the list. I will got with C++11 for Fluid and only poll opinions if C++17 would improve code greatly.

IMHO our goal to release 1.5.0 should be in this year (2025).
I will work toward that! 

 - Matthias

imm

unread,
Mar 2, 2025, 11:37:43 AMMar 2
to coredev fltk
On Sun, 2 Mar 2025, 14:43 'Albrecht Schlosser' via fltk.coredev
Ian, does this "template stuff" include std::string? Does this mean that we should NOT use std::string in our future API?

TBH, I don't know. I only know I was burned by this "in the past", and I think I remember Greg complaining about it here too (long ago...)

Sometime within the last few weeks I was looking something up on msdn and stumbled across a section exhorting users *not* to put STL stuff in API because of issues with msvc versions & compatibility (probably across dll boundaries, based on what I think I was researching at the time!)
That said, I can't find the page now.

Not a lot of help... Sorry.

Albrecht Schlosser

unread,
Mar 2, 2025, 11:40:00 AMMar 2
to fltkc...@googlegroups.com
Thanks, Matthias, for supporting this transition and, of course, for all your work for FLTK.

Albrecht

Albrecht Schlosser

unread,
Mar 2, 2025, 11:43:38 AMMar 2
to fltkc...@googlegroups.com
Thanks for replying anyway.

This sounds like something we need to research...

Greg Ercolano

unread,
Mar 2, 2025, 12:16:11 PMMar 2
to fltkc...@googlegroups.com


On 3/2/25 08:37, imm wrote:
On Sun, 2 Mar 2025, 14:43 'Albrecht Schlosser' via fltk.coredev
Ian, does this "template stuff" include std::string? Does this mean that we should NOT use std::string in our future API?

    I was actually wondering about this myself, and apparently Ian remembers my remarking about it (but I don't, lol)..


TBH, I don't know. I only know I was burned by this "in the past", and I think I remember Greg complaining about it here too (long ago...)

    Sometimes my memory is bad, but usually not on issues like this.

    I sat for about 10 secs trying to remember what this might possibly be, and I think maybe what Ian's remembering is when I discovered 'inline' functions/methods in #include files later converted to non-inline can affect ABI.

    Ian, does that sound like what you're thinking I ran into? I remember this being a revalation for some, certainly was for me. This was probably around the time I started messing with that ABI checker some years ago.

    Anyway, templates might be affected similarly if they too go through an inline / non-inline transition that affects ABI. Ian, perhaps you were able to extrapolate the effect on templates where I maybe did not. (When I've used templates, I'm never concerned about ABI, as I don't generally write libraries, just applications).

    Trying to think how a template/inline issue in std might affect us wrt to ABI.

    If our use of std templates in our #include files gets "baked" into a compiled FLTK lib, and then that lib ends up on a machine that has a slightly different C/C++ dynamic library with a different inline for those cases, I would think that issue would be managed by the C/C++ library versioning?

    In other words, not our problem, but a problem managed by the C/C++ library packaging and versioning?

    At least I think.



Sometime within the last few weeks I was looking something up on msdn and stumbled across a section exhorting users *not* to put STL stuff in API because of issues with msvc versions & compatibility (probably across dll boundaries, based on what I think I was researching at the time!)


    Seriously hoping that is old info, where microsoft is trying to document problems with old libs.

    I might not be understanding what those microsoft docs are getting at wrt to versioning, I'm assuming they're talking about library versioning as an ABI management issue, just as we have to do for maintenance releases.

    Perhaps Microsoft is documenting a warning about /old/ C/C++ library issues, where they didn't carefully check ABI incompatibilty that modern ABI checkers can now do? Assuming Microsoft has their ducks in a row now with C/C++ library versioning, their solving that problem would protect us too.

    But I don't know for sure.

    I would think this would be an issue for ANY library that uses templates (e.g. std) -- it seems unlikely that could still be an issue, or microsoft apps that use lots and lots of libraries (like web browsers) would be crashing all over the place.

<digression>
    Still, there is the problem with the affect of templates on debugging. Have you ever tried running a debugger and stepped through the std stuff? lol wotta nightmare; due to templating their crazy code ends up being expanded into your code, and it's hard to predict as you're stepping through code. Sure if you see "std::string foo;" you'll know to step over it, but if you see "x = foo + bar;" you might not know "foo" is a std variable.

    Not to mention compiler errors about templated code use; template errors are horrible. Maybe the compilers are better at this now, but I recall getting paragraphs of unreadable error messages from one simple typo in code that involved a std template.

    I could say more on those subjects, but won't. Could also be dated info, as compilers now embrace std, and have gotten much better at error reporting and checking. They might report less confusing errors about typos with std stuff.


</digression>

Greg Ercolano

unread,
Mar 2, 2025, 1:22:52 PMMar 2
to fltkc...@googlegroups.com


On 3/2/25 08:43, 'Albrecht Schlosser' via fltk.coredev wrote:
This sounds like something we need to research...


    Someone replied to me privately with this link:
    https://stackoverflow.com/questions/5661738/how-can-i-use-standard-library-stl-classes-in-my-dll-interface-or-abi

    While the question was asked 13 yrs ago, there's followups with recent dates, so apparently still an issue.

    And if I squint my brain, it's not hard to see how templates, basically complex macros, expanded at compile time might be incompatible with precompiled templated code, because templates are expanded at build time.

    So if a pre-compiled FLTK lib is present on a machine that was compiled with compiler verison X, and the user compiles against it with compiler version Y, the std templates of X might not use compatible code with version Y, and therefore hopelessly incompatible.

    And AFAIK, when you #include <string> there's no versioning going on there, unless perhaps we somehow can encode the STL (oops I mean std) version# into our #include files, so it could at least /detect/ an incompatibility. Perhaps there's a way to do that so the user gets an error or warning, but probably no way to /solve/ that one.

    So it sure sounds like a big NO for exposing /any/ std stuff in the API, public or protected. Perhaps not even private! (Since just delcaring a std::string might be a compile time template expansion).

    So that all sucks. I never really liked the STL library, and now I have a new reason to dislike it.

    Perhaps a workaround is to stick with our own Fl_String and Fl_Array (or whatever), and implement it around a private instance of the std equivalent, though I'm not sure we can even do that if we can't even declare the std::string in our .H file.. how else could we instance it?

    IIRC, libraries like Qt have their own string and array classes (QString, etc) and only uses that in their API calls. I assumed that was because STL wasn't well supported, but now that it is, I imagine they're still sticking with their implementations for this reason.

    So, like, wow.

Greg Ercolano

unread,
Mar 2, 2025, 1:52:41 PMMar 2
to fltkc...@googlegroups.com

On 3/2/25 10:22, Greg Ercolano wrote:

Perhaps a workaround is to stick with our own Fl_String and Fl_Array (or whatever), and implement it around a private instance of the std equivalent, though I'm not sure we can even do that if we can't even declare the std::string in our .H file.. how else could we instance it?


    Or, we just continue as we have, keeping the C interface in the API, dump our private Fl_String (etc) in favor of using std internally, and never #include any std stuff in our public .H files.

    As long as the std stuff is expanded at the time the library is built, we should be OK.

    We'd have to be careful about returning C style strings, e.g. with c_str(), and that might be tricky if we can't instance the string via the .H files to prevent the variable going out of scope during the return. Ick.

    Also I'm not sure if there's any way to provide C style arrays from a std::vector though, without making a copy or some such.

    Jeez, this just sounds messy all the way around.

    The only clean way out I see (other than to leave everything as is), is to make our own Fl_String and Fl_Array classes so we can use them publicly, and implement it with our own template code. Or merge a third party string + array class into our code like we do with e.g. the svg code.

    Curious what others think.

Albrecht Schlosser

unread,
Mar 2, 2025, 3:02:32 PMMar 2
to fltkc...@googlegroups.com
On 3/2/25 19:52 Greg Ercolano wrote:

   Also I'm not sure if there's any way to provide C style arrays from a std::vector though, without making a copy or some such.

    Jeez, this just sounds messy all the way around.

    The only clean way out I see (other than to leave everything as is), is to make our own Fl_String and Fl_Array classes so we can use them publicly, and implement it with our own template code. Or [...]

    Curious what others think.


What I think is: the more I read about this subject, the more I'm confused.

Here's an example:

https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-1-c4251?view=msvc-170#example

Example code copied for clarity:
```
// C4251.cpp
// Compile with /std:c++20 /EHsc /W2 /c C4251.cpp
#include <vector>

class __declspec(dllexport) X
{
public:
    X();
    ~X();

    void do_something();

private:
    void do_something_else();
    std::vector<int> data; // warning c4251
};
```

Warning c4251 is described in the "Remarks" section: "This warning happens if a class is marked with __declspec(dllexport) or __declspec(dllimport) and a nonstatic data member that is a member of the class or a member of one of its base classes, has a type that is a class type that isn't marked with __declspec(dllexport) or __declspec(dllimport)".

Why is this example relevant? Because we're using such constructs in our classes and blindly replacing Fl_Int_Vector (or IntVector, as it was in FLTK 1.3) will create exactly the same issue. I'm using code from 1.3 here because it's slightly different than 1.4 code where the embedded IntVector instance is now a pointer. My plan was to "return" to the implementation of 1.3 with an embedded std::vector<int> instead of the pointer used in 1.4 (the latter was chosen only to be able to use a hidden class (Fl_Int_Vector) that's not in the public API). Hence, replacing IntVector in the 1.3 code would result in something like:
```
FL/Fl_Table.H:170:class FL_EXPORT Fl_Table : public Fl_Group {
FL/Fl_Table.H-171-public:
...
FL/Fl_Table.H:257:  std::vector<int> _colwidths;   // column widths in pixels [1]
FL/Fl_Table.H:258:  std::vector<int> _rowheights;  // row heights in pixels   [1]
...
```

Looks similar, doesn't it? Note that FL_EXPORT expands to __declspec(dllexport).

Microsoft has a recipe to avoid this warning (which is there for a reason): "To fix this warning, don't mark the class with __declspec(dllexport) or __declspec(dllimport). Instead, only mark the methods that are used directly by a client."

Resultant (fixed) code would look like this:
```
// C4251_fixed.cpp
// Compile with /std:c++20 /EHsc /W2 /c C4251-fixed.cpp
#include <vector>

class X
{
public:
    __declspec(dllexport) X();
    __declspec(dllexport) ~X();

    __declspec(dllexport) void do_something();

private:
    void do_something_else();
    std::vector<int> data;
};
```

Awesome!

Note: first I thought we'd get this warning if we compiled FLTK (1.4/1.5) with FLTK_OPTION_STD:BOOL=ON but then I realized that we currently have the pointer rather than the embedded variable. Therefore it's not as easy, but I guess we'd get the compiler warning if we changed it as intended.

That all said, the conclusion could really be to do what you, Greg, wrote above:

"The only clean way out I see (other than to leave everything as is), is to make our own Fl_String and Fl_Array classes so we can use them publicly ...".

But that doesn't solve the issue with embedded "nonstatic members of" std::vector<int> although std::vector<int> is publicly available.

Awesome!

Sorry for the rant.


-----
[1] In 1.3.x this is an instance of the internal 'class IntVector' which *is* exported by using FL_EXPORT:
```
FL/Fl_Table.H:223:  class FL_EXPORT IntVector {
...
FL/Fl_Table.H:257:  IntVector _colwidths;   // column widths in pixels
FL/Fl_Table.H:258:  IntVector _rowheights;  // row heights in pixels
```
Maybe that's the reason why class IntVector was "exported" in the first place? I dunno.

duncan

unread,
Mar 3, 2025, 2:52:38 AMMar 3
to fltk.coredev
Just to add my two cents about std::string

Users may perceive std::string as more singing and dancing than plain char* null-terminated strings,
but a std::string is still just a container of bytes, and you still need to do some work to handle UTF-8.
May I suggest a major review and potential overhaul of the fl_utf8 code and documentation early on?


melcher....@googlemail.com

unread,
Mar 3, 2025, 6:36:02 AMMar 3
to fltk.coredev
tl;dr: Sorry, C++ has no standardized ABI. You simply can't link a library compiled with one compiler with an executable compiler with a different compiler (or compiler version).

C++ libraries compiled with GNU gcc < 5.0 are incompatible to gcc > 5.0, VisualC <= 2019 is binary incompatible to VC > 2019. std::string may be binary incompatible between clang, gcc, and vc, and between versions of the same compiler. For example, std::string_view is sometimes implemented a pointer/size, and sometimes as start_pointer/end_pointer. Even the way C++ function signatures are converted into assembler  labels is not standardized (name mangling, decorated names).

So unless we want to limit ourselves to pure "C", or rewrite everything in Rust, the best we can do is help the user discover if the FLTK library is compiled with the exact same compiler and version that is used to compile the user's app. Everything else is UB.

Extending this, if we insist on the "same compiler", we can use anything that this compiler provides in our public headers.

imacarthur

unread,
Mar 3, 2025, 8:56:06 AMMar 3
to fltk.coredev
On Monday, 3 March 2025 at 11:36:02 UTC Matt wrote:

C++ libraries compiled with GNU gcc < 5.0 are incompatible to gcc > 5.0,

I believe this is true, but (since I'm supporting code with huge variety of ancestry....) what I find *in practice* is that I can pretty much link gcc C++ code from different generations and have it work. 
As a particular example, one of our old projects is tied to an ancient toolchain that uses gcc-4.3 (which is *old*) but it shares functionality with another project that is "less old" and uses gcc-6.3 and, at least for static linking of .o files and .a archives, the linking works and the code runs.
Also, AFAIK, the name-mangling used by gcc and clang _appears_ to be compatible. (I've mixed code from gcc and clang (llvm-12) and it seemed to be OK.)
I think the last time I saw gcc API break badly was in the 3.2 to 3.3 transition, and in the 2.95 to 3.0 transition before that. Since then it does seem to have "worked", for the most part!
That said, I'm only using "simple stuff" in the API. I'm not using any std:: stuff in the API, so I do not know how consistent that has been.
AFAICT, using the std:: stuff "inside" the code seems to be OK - but...

 
VisualC <= 2019 is binary incompatible to VC > 2019.

"Older" VC versions were, in my experience, liable to change ABI at each iteration; I've been hit by that. The newer stuff seems "more stable" though I confess I remain wary.
 
std::string may be binary incompatible between clang, gcc,

Though in my (*very limited*) testing, the clang std:: stuff appeared to be "compatible" with whichever gcc I had been using at the time (forget now what, might have been gcc-9.something...)
 
and vc, and between versions of the same compiler.

This is certainly true, as I have suffered from this!
 
For example, std::string_view is sometimes implemented a pointer/size, and sometimes as start_pointer/end_pointer. Even the way C++ function signatures are converted into assembler  labels is not standardized (name mangling, decorated names).

So unless we want to limit ourselves to pure "C", or rewrite everything in Rust, the best we can do is help the user discover if the FLTK library is compiled with the exact same compiler and version that is used to compile the user's app. Everything else is UB.

Extending this, if we insist on the "same compiler", we can use anything that this compiler provides in our public headers.

I *think* it is mainly the DLLs (or other shared objects) that are the problem. I've not had problems with static code, but I have with DLL/SO code.
So if we are going to build DLLs at all, do we need to do the thing that MS used to do, and provide (the means to build) a versioned DLL per-compiler variant? 
And of course there's the historical problem that (although gcc and clang seem to mostly play quite nicely together) any DLL still will not work between gcc and VC.

Bob Tolbert

unread,
Mar 3, 2025, 9:07:05 AMMar 3
to fltkc...@googlegroups.com
Lurker here, but I'm a 25+ year FLTK user and I spent the last 20+ years evolving a commercial C++ code base from C/C++98-ish to C++17. The two things that always made me fall back to FLTK (over other GUI TKs) were the fast compile times and API stability. When a new version appeared, I just re-compiled everything. Never worried or thought about ABI in C++ - therein lies madness

On Mon, Mar 3, 2025 at 5:36 AM 'melcher....@googlemail.com' via fltk.coredev <fltkc...@googlegroups.com> wrote:
tl;dr: Sorry, C++ has no standardized ABI. You simply can't link a library compiled with one compiler with an executable compiler with a different compiler (or compiler version).

This can even be between smaller version differences with GCC gcc 7/8 vs gcc 9/10 for example having different ABI in the libstdc++. We shipped static libs (closed-source code) so we had different distributions for different C++ compilers and versions.In the case of open source, if people can build their own, with their own compiler, then you can support the widest range of compilers. We would have loved that option.

What we did work very hard to do is maintain API compatibility so that a re-compile was all that was needed. As we introduced std::string for example, we overloaded methods that previously used const char* to avoid API changes while allowing new code to use std::string. When interacting with FLTK, using both const char* and std::string and Fl_String was a real pain point and I know involved a ton of extra allocations that would have been nice to avoid.

Ultimately, though, the progress in C++ compilers meant we had to keep ratcheting how many versions we could support behind us. Trying to keep a C++ code base working from gcc5 to gcc13 is a ton of work. I suspect a "C" code base is easier, though I have no experience.
 

C++ libraries compiled with GNU gcc < 5.0 are incompatible to gcc > 5.0, VisualC <= 2019 is binary incompatible to VC > 2019. std::string may be binary incompatible between clang, gcc, and vc, and between versions of the same compiler. For example, std::string_view is sometimes implemented a pointer/size, and sometimes as start_pointer/end_pointer. Even the way C++ function signatures are converted into assembler  labels is not standardized (name mangling, decorated names).

So unless we want to limit ourselves to pure "C", or rewrite everything in Rust, the best we can do is help the user discover if the FLTK library is compiled with the exact same compiler and version that is used to compile the user's app. Everything else is UB.

I mostly came here to agree with this. It feels like early FLTK was almost "C with classes" and that still amazes me how fast it compiles and how fast it can run. That said, if there is reticence to use std::string in the API then it seems any evolution toward more modern C++ will be hard. In some ways the code base is closer to pure "C" and might make more sense to move in that direction instead.


For our internal code we moved first to C++11 then later to C++17. We didn't expose too many API points that exposed internal std::* implementations, preferring to keep the implementation hidden for future changes.

I think it was a huge win for usability to overload an API like and this doesn't break API compatibiilty:
    void SetTitle(const char* t);
    void SetFoo(float *x, unsigned int length);
with
    void SetFoo(const std::string& t);
    void SetFoo(const std::vector<float>& x);

The question for us was always whether to introduce both of these API styles into a new class. Just because we could overload to modernize, we really didn't want to perpetuate the old style in brand new APIs

But all the other gains from C++17 like <memory> for safer pointers, std containers and <algorithm> to prevent rolling your own and lambdas for callbacks could be big wins for FLTK or too much of a break with the past.

Extending this, if we insist on the "same compiler", we can use anything that this compiler provides in our public headers.

and I applaud the move to CMake. While not perfect, it is useful. We also moved our old code base from hand-rolled configure/Makefile to old CMake and then had to do another migration to "Modern CMake". Starting fresh now, I think CMake is decent and does have very active development.
 
--
You received this message because you are subscribed to the Google Groups "fltk.coredev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkcoredev...@googlegroups.com.

melcher....@googlemail.com

unread,
Mar 3, 2025, 9:45:48 AMMar 3
to fltk.coredev
Thank you. To me this was a very helpful answer (not only about C++, but also nice to see that we have lurking long timers ;-). This reinforces the original idea to require at least C++11, introduce std::string and std::vector into the API, and see ho it goes.

I see 1.5 as an introduction of slightly more modern C++ without too many new features, so users can fall back to 1.4 for the time being. As Albrecht said, we want 1.5 out before the end of the year and get quick feedback. If users have no issues with the extend API, on to 1.6 ;-) .

Albrecht Schlosser

unread,
Mar 3, 2025, 9:47:20 AMMar 3
to fltkc...@googlegroups.com
On 3/3/25 12:36 'melcher....@googlemail.com' via fltk.coredev wrote:
tl;dr: Sorry, C++ has no standardized ABI. You simply can't link a library compiled with one compiler with an executable compiler with a different compiler (or compiler version).

If this is still the case (and I believe you that it is) then it's a big mess. <rant> What is all the standardization of the *Language* worth if different compilers can't cooperate? </rant>


C++ libraries compiled with GNU gcc < 5.0 are incompatible to gcc > 5.0,

I wouldn't care about such old compilers anymore (if this helped).


VisualC <= 2019 is binary incompatible to VC > 2019.

That would indeed be a problem.


std::string may be binary incompatible between clang, gcc, and vc, and between versions of the same compiler. For example, std::string_view is sometimes implemented a pointer/size, and sometimes as start_pointer/end_pointer.

Really bad.


Even the way C++ function signatures are converted into assembler labels is not standardized (name mangling, decorated names).

If this was (still) the case I wonder how any Linux distro could provide a shared FLTK library. Every distro must decide to use one particular compiler (maybe the current - or oldest supported - GCC compiler of that distribution). Let's assume the Debian distro uses GCC (any version) to build the shared FLTK library which can be installed in binary form. If name mangling didn't work the same when a user program is compiled with clang, then no user could compile their programs with clang and link to the system supplied shared FLTK library (compiled with GCC). Do you really believe that this is still the case? I could try this, but not right now (and if I did, this wouldn't proof that *all* combinations of compilers can cooperate).


So unless we want to limit ourselves to pure "C", or rewrite everything in Rust, the best we can do is help the user discover if the FLTK library is compiled with the exact same compiler and version that is used to compile the user's app. Everything else is UB.

Extending this, if we insist on the "same compiler", we can use anything that this compiler provides in our public headers.

Insisting on the "same compiler" would be hard to do for users deploying their applications as binaries. Static linking could be an option, but are we sure that all template code is compiled together with the program/library and nothing is used from a shared library (C++ runtime) on the target system?

Below I'm posting another, very simple example of our potential future use of std::vector in FL/Fl_Group.H. Note that this is only one example where std::vector would (or could) be used in a public header (I posted an example of Fl_Table in a previous comment but there are more examples).

```
#include <vector>
...
class FL_EXPORT Fl_Group : public Fl_Widget {
std::vector<Fl_Widget *>child_; // vector of children
...

    ```

where std::vector replaces the union of pointers we have now (in 1.4):

```
class FL_EXPORT Fl_Group : public Fl_Widget {
union {
Fl_Widget** array_; // used if group has two or more children or NULL
Fl_Widget* child1_; // used if group has one child or NULL
};
...

    ```

The code above can be found in my fork in branch Fl_Group-vector (https://github.com/Albrecht-S/fltk/tree/Fl_Group-vector):
https://github.com/Albrecht-S/fltk/blob/933cad897a219248a9b63323456cbb3844c447da/FL/Fl_Group.H#L61

(The new code is obviously not ABI compatible to 1.4 but that's normal and doesn't matter here.)

Although the vector child_ is private it is included in the binary layout (ABI) of Fl_Group, compiled together with the executable and the (potentially shared) library.

Static linking aside, what we need to consider is only the "worst case" [1]: users on a given system want to build their apps with either gcc or clang (or any other potential compiler on that system) and link to the shared FLTK library provided by the system, for instance on Debian that provides a shared FLTK library.

Let's also think about binary "distributions" (package managers) on Windows where potentially shared libs (DLL's) are provided as well. Homebrew on macOS is similar.

AFAICT nobody complained so far about binary incompatibilities for existing FLTK versions, i.e. 1.3 and 1.4, where we don't use std:: stuff (which means that name mangling is probably not an issue anymore).

But what would happen if we changed Fl_Group as shown above (in my fork)? Would we force our users to use the same compiler for their FLTK libraries and their executables?

Would nobody be able to build their apps with clang and link against the shared system FLTK library on Debian (for instance) which is presumably built with gcc (or vice versa)?

That all said: I believe we need a decision to a "simple" question: Do we allow template stuff like std::string and std::vector in public FLTK headers?

If yes, what would be the consequences (e.g. "same compiler"), and how to document these?

If no, the consequence would be "ugly" code that uses pointers and 'new'  to allocate stuff like the vector of children in the .cxx file and thus to "hide" the existence of the vector in the public header and ABI. Suggestions how to deal with this in general would be very much appreciated.

-----
[1] Even "worse than the worst case" (TM) would be if users wanted to deploy executables (binaries) linked against a shared library to another system but I believe we can ignore this - this would be undefined behavior anyway (if it worked at all). The only issue to consider: if FLTK 1.5 imposed new restrictions, then users might consider this a regression.

Albrecht Schlosser

unread,
Mar 3, 2025, 10:09:15 AMMar 3
to fltkc...@googlegroups.com
On 3/3/25 15:47 'Albrecht Schlosser' wrote:
I believe we need a decision to a "simple" question: Do we allow template stuff like std::string and std::vector in public FLTK headers?

For clarification: the statement and question above is about the pure existence of e.g. std::vector in the public header, and more precisely: whether an instance of std::vector, std::string, etc. is an embedded member of the class (no matter if it is private or protected).

Such members shouldn't be public anyway (there should be public accessors), but if they were, then this would be in the public API which we *may* not allow in FLTK 1.5 - although maybe we should, as Matthias wrote in his recent comment.

Albrecht Schlosser

unread,
Mar 3, 2025, 10:21:56 AMMar 3
to fltkc...@googlegroups.com
On 3/3/25 15:45 Matthias wrote:
Thank you. To me this was a very helpful answer (not only about C++, but also nice to see that we have lurking long timers ;-).

I can only second that. Thanks for your comments, Bob.

This reinforces the original idea to require at least C++11, introduce std::string and std::vector into the API, and see how it goes.

+1


I see 1.5 as an introduction of slightly more modern C++ without too many new features, so users can fall back to 1.4 for the time being. As Albrecht said, we want 1.5 out before the end of the year and get quick feedback. If users have no issues with the extend API, on to 1.6 ;-) .

+1

I agree again. Although I wrote about compatibility issues in my previous post I would really like to begin using at least the most basic stuff (std::string and std::vector) in the public API and not only internally. As we can see, the pure existence as a member of a class like Fl_Group (in its public header) can change the ABI, so why not use it in the public API as well?

As Bob wrote, since we always take care of backwards compatibility, this would be done only in some cases where it is really useful and can be expected by users, and only by extending the existing API.

Greg Ercolano

unread,
Mar 3, 2025, 10:52:27 AMMar 3
to fltkc...@googlegroups.com

But what would happen if we changed Fl_Group as shown above (in my fork)? Would we force our users to use the same compiler for their FLTK libraries and their executables?

Would nobody be able to build their apps with clang and link against the shared system FLTK library on Debian (for instance) which is presumably built with gcc (or vice versa)?

    Can we record the compiler version#/name in our public headers as part of the lib build process, and add warnings if they don't match when someone builds against the pre-built library?

    I imagine this can all be done with macro logic.. would have to be very compiler specific though, and be a bit of a maintenance issue, but it might then let us use std freely.

Bob Tolbert

unread,
Mar 3, 2025, 11:05:26 AMMar 3
to fltkc...@googlegroups.com
This is absolutely what we did, even though we only shipped static libraries. We would rather have our C++ toolkit customers get a compile time failure (that we could document) vs. undefined runtime weirdness where the link worked but the code didn't really run correctly.
 
-- 
You received this message because you are subscribed to the Google Groups "fltk.coredev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkcoredev...@googlegroups.com.

Bob Tolbert

unread,
Mar 3, 2025, 11:23:55 AMMar 3
to fltkc...@googlegroups.com
On Mon, Mar 3, 2025 at 8:47 AM 'Albrecht Schlosser' via fltk.coredev <fltkc...@googlegroups.com> wrote:
On 3/3/25 12:36 'melcher....@googlemail.com' via fltk.coredev wrote:


std::string may be binary incompatible between clang, gcc, and vc, and between versions of the same compiler. For example, std::string_view is sometimes implemented a pointer/size, and sometimes as start_pointer/end_pointer.

Really bad.

I don't think this is bad. This is different compilers taking different approaches to optimization. 'std::string_view' is an area for optimization to avoid unnecessary copies. As long as the API is standardized and people use the API, I'm not sure I see the downside of different implementations internally. 
 

Even the way C++ function signatures are converted into assembler labels is not standardized (name mangling, decorated names).

If this was (still) the case I wonder how any Linux distro could provide a shared FLTK library. Every distro must decide to use one particular compiler (maybe the current - or oldest supported - GCC compiler of that distribution). Let's assume the Debian distro uses GCC (any version) to build the shared FLTK library which can be installed in binary form. If name mangling didn't work the same when a user program is compiled with clang, then no user could compile their programs with clang and link to the system supplied shared FLTK library (compiled with GCC). Do you really believe that this is still the case? I could try this, but not right now (and if I did, this wouldn't proof that *all* combinations of compilers can cooperate).

I wonder whether a "system" library written in C++ is almost an oxymoron. I'm sure there are examples, but it feels like most Linux system libs are C not C++. Certainly, there are Qt libs on machines, but frankly if you want to use the system Qt you probably have to use the system C++ as well - or handle the cross-compatibility yourself.

 

So unless we want to limit ourselves to pure "C", or rewrite everything in Rust, the best we can do is help the user discover if the FLTK library is compiled with the exact same compiler and version that is used to compile the user's app. Everything else is UB.

Extending this, if we insist on the "same compiler", we can use anything that this compiler provides in our public headers.

Insisting on the "same compiler" would be hard to do for users deploying their applications as binaries. Static linking could be an option, but are we sure that all template code is compiled together with the program/library and nothing is used from a shared library (C++ runtime) on the target system?


Certainly if I want to deploy on Linux, I'd be very careful about deps. DLL-hell has certainly become a problem on modern Linux. And a lot of the drive for folks to use Go or Rust is dependency management and the ability to avoid all these shared lib dep problems when deploying. Even on macOS we avoided the homebrew shared FLTK in favor of building our own with a known compiler and then statically linking it in.  Yes, this does lead to the narrow case where using system libs that then get security updates means our app gets a security update, but we never really experienced that IRL. Frankly, we used Qt for a large commercial app since it was already in place but for any internal tool, I always used statically linked FLTK since I could just give someone an executable and they were running.

If wide ABI compatibility is the ultimate goal vs. long-term API stability (so re-compiles work) then I think C++ in any form is inappropriate. 

FLTK seems so much closer to "C with classes" than modern C++, that trying to be both will be hard. A strawman idea would be to move the core to C only then provide an FLTK++ library that uses modern C++ and wraps the C API. This way you could ship a much more stable C API/ABI core lib and the C++ as a wrapper. (Sadly this would break API on both the C and C++ side but would provide a path to a stable ABI)

This would be a ton of work but would also enable more languages via their C FFI. But, wow, that would be a ton of work.


--
You received this message because you are subscribed to the Google Groups "fltk.coredev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkcoredev...@googlegroups.com.

Albrecht Schlosser

unread,
Mar 3, 2025, 11:24:24 AMMar 3
to fltkc...@googlegroups.com
On 3/3/25 17:05 Bob Tolbert wrote:

On Mon, Mar 3, 2025 at 9:52 AM Greg Ercolano wrote:

    Can we record the compiler version#/name in our public headers as part of the lib build process, and add warnings if they don't match when someone builds against the pre-built library?

    I imagine this can all be done with macro logic.. would have to be very compiler specific though, and be a bit of a maintenance issue, but it might then let us use std freely.

This is absolutely what we did, even though we only shipped static libraries. We would rather have our C++ toolkit customers get a compile time failure (that we could document) vs. undefined runtime weirdness where the link worked but the code didn't really run correctly.

Is there code you can (i.e. are allowed to) post that gets the compiler name and version for us as a starter so we don't need to reinvent the wheel? Even if you are using closed source, this is maybe not an issue for such "simple macro stuff". Contributions are always appreciated (and thanks again for your previous post). Thanks in advance.

But if not, never mind, this shouldn't be too difficult.

Mohammed

unread,
Mar 3, 2025, 11:51:56 AMMar 3
to fltkc...@googlegroups.com
Regarding the abi and using std types in the public headers, it’s mostly an issue on linux and only when using clang and linking libc++. By default, however, clang uses libstdc++ on linux and libc++ on macos. So one rarely runs into abi issues and would have to explicitly link libc++ on linux when using clang.

In the msys2 environment, devs usually choose the standard c/c++ library when choosing the enviroment.
MINGW - msvcrt + libstdc++.
UCRT - ucrt + libstdc++.
CLANG - ucrt + libc++.
All packages provided by pacman will be built for that specific environment so you’re less likely to run into abi issues.

On windows outside of msys2, clang uses the default msvc abi which has been stable for the last few years, and mixing and matching libs built with clang and msvc should work normally.

Bob Tolbert

unread,
Mar 3, 2025, 12:12:39 PMMar 3
to fltkc...@googlegroups.com
I'm happy to make a branch that can do this, at least a first pass, just so we can see what it might look like. Certainly we did allow ranges of versions when we knew (had tested) that they were compatible.

and CMake makes it easier (for me) to do this.


--
You received this message because you are subscribed to the Google Groups "fltk.coredev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkcoredev...@googlegroups.com.

Albrecht Schlosser

unread,
Mar 3, 2025, 12:20:55 PMMar 3
to fltkc...@googlegroups.com
On 3/3/25 17:23 Bob Tolbert wrote:

On Mon, Mar 3, 2025 at 8:47 AM 'Albrecht Schlosser' via fltk.coredev <fltkc...@googlegroups.com> wrote:
On 3/3/25 12:36 'melcher....@googlemail.com' via fltk.coredev wrote:

std::string may be binary incompatible between clang, gcc, and vc, and between versions of the same compiler. For example, std::string_view is sometimes implemented a pointer/size, and sometimes as start_pointer/end_pointer.

Really bad.

I don't think this is bad.

I meant: from an ABI point of view.


This is different compilers taking different approaches to optimization. 'std::string_view' is an area for optimization to avoid unnecessary copies. As long as the API is standardized and people use the API, I'm not sure I see the downside of different implementations internally.

I agree with this "API point of view". That's why we have such implementations and should not (need to) care about the implementation.


Even the way C++ function signatures are converted into assembler labels is not standardized (name mangling, decorated names).

If this was (still) the case I wonder how any Linux distro could provide a shared FLTK library. Every distro must decide to use one particular compiler (maybe the current - or oldest supported - GCC compiler of that distribution). Let's assume the Debian distro uses GCC (any version) to build the shared FLTK library which can be installed in binary form. If name mangling didn't work the same when a user program is compiled with clang, then no user could compile their programs with clang and link to the system supplied shared FLTK library (compiled with GCC). Do you really believe that this is still the case? I could try this, but not right now (and if I did, this wouldn't proof that *all* combinations of compilers can cooperate).

I wonder whether a "system" library written in C++ is almost an oxymoron. I'm sure there are examples, but it feels like most Linux system libs are C not C++. Certainly, there are Qt libs on machines, but frankly if you want to use the system Qt you probably have to use the system C++ as well - or handle the cross-compatibility yourself.

I was only talking about FLTK, and Linux distros do ship packages with shared FLTK libs (.so's). I think that "real system libs" usually provide a C interface and that's good so.


Certainly if I want to deploy on Linux, I'd be very careful about deps. DLL-hell has certainly become a problem on modern Linux. And a lot of the drive for folks to use Go or Rust is dependency management and the ability to avoid all these shared lib dep problems when deploying. Even on macOS we avoided the homebrew shared FLTK in favor of building our own with a known compiler and then statically linking it in.

That's a good decision for your project, but not all FLTK users do this. We often see that FLTK users have issues compiling their apps and linking to the system provided FLTK libs (shared or static, both is the case).


If wide ABI compatibility is the ultimate goal vs. long-term API stability (so re-compiles work) then I think C++ in any form is inappropriate.

Well, as it stands, FLTK is C++ and we can't change this. ABI compatibility is a high goal within minor versions, for instance 1.3.x, but not between 1.4.x and 1.5.x.

FLTK seems so much closer to "C with classes" than modern C++, that trying to be both will be hard.A strawman idea would be to move the core to C only then provide an FLTK++ library that uses modern C++ and wraps the C API. This way you could ship a much more stable C API/ABI core lib and the C++ as a wrapper. (Sadly this would break API on both the C and C++ side but would provide a path to a stable ABI).

The entire idea of FLTK was in the first place to "convert" an existing C library to C++ (and extend it) to allow for inheritance (AFAICT, Matthias and Bill, please correct me if I'm wrong). Moving the core library back to C doesn't sound sensible - in our special case.


This would be a ton of work but would also enable more languages via their C FFI. But, wow, that would be a ton of work.

We know of a Rust port (fltk-rs on github) which is - AFAICT - based on a C API that *wraps* the FLTK C++ library (Mohamed, is this true?). I think there are maybe other language bindings that are based on pure C wrappers. But that's the opposite way and not applicable to the core FLTK library.

Anyway, thanks for all your comments, this is very much appreciated.

Albrecht Schlosser

unread,
Mar 3, 2025, 12:27:59 PMMar 3
to fltkc...@googlegroups.com
On 3/3/25 18:12 Bob Tolbert wrote:

On Mon, Mar 3, 2025 at 10:24 AM 'Albrecht Schlosser' ... wrote:

Is there code you can (i.e. are allowed to) post that gets the compiler name and version for us as a starter so we don't need to reinvent the wheel? Even if you are using closed source, this is maybe not an issue for such "simple macro stuff". Contributions are always appreciated (and thanks again for your previous post). Thanks in advance.

I'm happy to make a branch that can do this, at least a first pass, just so we can see what it might look like. Certainly we did allow ranges of versions when we knew (had tested) that they were compatible.

Awesome, thanks. Your experience with these compatibility ranges would be very helpful.


and CMake makes it easier (for me) to do this.

Yep, we can use CMake to determine the compiler and version, as far as possible. The next minor FLTK version 1.5 (i.e. the one we're talking about) will be CMake only which simplifies things for us as well.

Bob Tolbert

unread,
Mar 3, 2025, 12:29:25 PMMar 3
to fltkc...@googlegroups.com
On Mon, Mar 3, 2025 at 11:20 AM 'Albrecht Schlosser' via fltk.coredev <fltkc...@googlegroups.com> wrote:
On 3/3/25 17:23 Bob Tolbert wrote:


Well, as it stands, FLTK is C++ and we can't change this. ABI compatibility is a high goal within minor versions, for instance 1.3.x, but not between 1.4.x and 1.5.x.


That is a very reasonable goal.
FLTK seems so much closer to "C with classes" than modern C++, that trying to be both will be hard.A strawman idea would be to move the core to C only then provide an FLTK++ library that uses modern C++ and wraps the C API. This way you could ship a much more stable C API/ABI core lib and the C++ as a wrapper. (Sadly this would break API on both the C and C++ side but would provide a path to a stable ABI).

The entire idea of FLTK was in the first place to "convert" an existing C library to C++ (and extend it) to allow for inheritance (AFAICT, Matthias and Bill, please correct me if I'm wrong). Moving the core library back to C doesn't sound sensible - in our special case.

Yeah, I don't really think that is possible. If moving to pure C is not possible, and staying put is not reasonable, then a path toward modern C++ is the only conclusion. (which is where everything seems to be) so then the only decision is the slope of that change. The one good thing about C++ is even if FLTK 1.5 is only up to C++11, a user can use C++17 or later in their app with no problems.
 
This would be a ton of work but would also enable more languages via their C FFI. But, wow, that would be a ton of work.

We know of a Rust port (fltk-rs on github) which is - AFAICT - based on a C API that *wraps* the FLTK C++ library (Mohamed, is this true?). I think there are maybe other language bindings that are based on pure C wrappers. But that's the opposite way and not applicable to the core FLTK library.

Anyway, thanks for all your comments, this is very much appreciated.

you bet. Reading all the previous emails in the chain really caused a flashback. I struggled whether to weigh in but figured you could ignore me. This is a very important discussion and I'm glad to see it continuing.

 

--
You received this message because you are subscribed to the Google Groups "fltk.coredev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkcoredev...@googlegroups.com.

Rob McDonald

unread,
Mar 3, 2025, 12:31:53 PMMar 3
to fltk.coredev
On Monday, March 3, 2025 at 5:56:06 AM UTC-8 imacarthur wrote:

I believe this is true, but (since I'm supporting code with huge variety of ancestry....) what I find *in practice* is that I can pretty much link gcc C++ code from different generations and have it work. 
As a particular example, one of our old projects is tied to an ancient toolchain that uses gcc-4.3 (which is *old*) but it shares functionality with another project that is "less old" and uses gcc-6.3 and, at least for static linking of .o files and .a archives, the linking works and the code runs.
Also, AFAIK, the name-mangling used by gcc and clang _appears_ to be compatible. (I've mixed code from gcc and clang (llvm-12) and it seemed to be OK.)
I think the last time I saw gcc API break badly was in the 3.2 to 3.3 transition, and in the 2.95 to 3.0 transition before that. Since then it does seem to have "worked", for the most part!
That said, I'm only using "simple stuff" in the API. I'm not using any std:: stuff in the API, so I do not know how consistent that has been.
AFAICT, using the std:: stuff "inside" the code seems to be OK - but...

I recently went down the path of trying to get a MacOS program to link with some parts built with gcc and other parts built with clang / xcode.  If both compilation units use std::, it really isn't possible.  Fundamentally, this is a libc++ vs libstdc++ problem.

Although there are hacks to gcc to point at the system SDK (libc++), it doesn't work in real life.  The upstream gcc project does not support libc++, so they have no problem breaking things without warning.  When Homebrew (or some other project) comes along with a fix, they have to fight an uphill battle to get the changes (for an unsupported platform) upstreamed.

In practice, using std:: will limit us to using one compiler per platform.  If this is not OK, then some sort of MWE test project should be constructed before major changes to FLTK are undertaken.  This test project should then be used to exercise all the toolchain combinatorics that are targeted to see if it is even possible.



I am not an FLTK dev -- just a user.  So I don't really think I deserve a vote here.  However, I do have a few thoughts I'd like to share....



I absolutely abhor 'auto'.  Some claim it makes code more readable -- in fact it does the opposite.  Declaring types is fundamentally documentation.  You're documenting (for future developers) exactly what type a variable has -- what information does it contain?  what methods and fields are available?  it it const or not?  etc.

When you use 'auto', you're either saying either:
1) I'm too lazy to document the type here. or
2) I'm not clever enough to figure out the type here.

Both are bad.  Unlike other documentation, declaring types has the advantage that it can not go out of date (as correctness is enforced at compile time).



I am sympathetic to Greg and other developers who must support users on antiquated platforms.  Some of my users finally were forced to update from RHEL7 and it was a big step forward.  However, choosing to maintain support for deprecated operating systems is potentially a large burden on FLTK's team.  In my experience 'many tools' choose to limit support to the OS versions that are still supported (receiving security updates) from the vendor.  This trickles down (compilers, Homebrew, Github Actions, etc) such that supporting deprecated platforms may limit our ability or willingness to use continuous integration tools.

I'm not saying that FLTK should not run on old iron.  Old programs on old platforms can continue to use FLTK 1.4, 1.3 or 1.1.  I simply think we should think hard about the Venn diagram of Win7 / OSX10.4 users who need to run the latest version of FLTK 1.5 apps.  It even seems possible that Apple will stop supporting Intel processors before FLTK 1.5 is finished and released.



In my experience, libraries that expose their template system to the user are almost always 'header only'.  This effectively eliminates any potential incompatibility with pre-compiled components of any sort.  This also effectively forces static linking for said library.

A header-only FLTK would result in smaller binaries vs. static linking -- because only the components used by an app would get built and incorporated.  FLTK might actually be faster and lighter that way.  However, it would ditch any / all benefits of dynamic linking on platforms that use it.

In my experience, dynamic linking of FLTK "at scale" is a myth anyway.
1) if you ship your own *.dll or *.so dynamic libraries with your app, you might as well be statically linking.
2) platforms that provide system shared libraries (i.e. Linux) are hopelessly out of date in what they package
3) platforms that provide system shared libraries (Linux distributions) are hopelessly incompatible with one another


Thanks for all your work,

Rob

 

melcher....@googlemail.com

unread,
Mar 3, 2025, 12:35:39 PMMar 3
to fltk.coredev


coho...@gmail.com schrieb am Montag, 3. März 2025 um 17:23:55 UTC+1:
A strawman idea would be to move the core to C only then provide an FLTK++ library that uses modern C++ and wraps the C API. This way you could ship a much more stable C API/ABI core lib and the C++ as a wrapper. This would be a ton of work but would also enable more languages via their C FFI. But, wow, that would be a ton of work.

As a thought experiment,  if the C++ API is the header-only, we avoid any and all C++ issues. But that would require all library code to be written in pure C as well, making core development even more painful. Is that even remotely realistic? Then again, we have our hand rolled std::everything code anyway already.

Albrecht wrote:
> The entire idea of FLTK was in the first place to "convert" an existing C library to C++ (and extend it) to allow for inheritance (AFAICT, Matthias and Bill, please correct me if I'm wrong). Moving the core library back to C doesn't sound sensible - in our special case.
> We know of a Rust port (fltk-rs on github) which is - AFAICT - based on a C API that *wraps* the FLTK C++ library (Mohamed, is this true?).

Everything above is correct, and it's hilarious in a way. I wish that instead of adding esoteric crap to C++26, the committee would have forcused an making C++ feasible for supporting libraries by defining the ABI, but we are as far es we can be from that. I am following the Rust vs. C discussions around the Linux Kernel a little bit, and it's a stormy path. But a sure fire way to be kicked out of any discussion is to just mention C++. Amazing to me how a language can make itself obsolete.

Albrecht Schlosser

unread,
Mar 3, 2025, 1:17:18 PMMar 3
to fltkc...@googlegroups.com
On 3/3/25 18:29 Bob Tolbert wrote:

On Mon, Mar 3, 2025 at 11:20 AM 'Albrecht Schlosser' ... wrote:
On 3/3/25 17:23 Bob Tolbert wrote:


Well, as it stands, FLTK is C++ and we can't change this. ABI compatibility is a high goal within minor versions, for instance 1.3.x, but not between 1.4.x and 1.5.x.


That is a very reasonable goal.

Thanks for your confirmation. That's written in our CMP as long as I remember using FLTK (i.e. far before I joined the team).
https://www.fltk.org/cmp.php#SR_MINOR_RELEASES

(Note: this entire page needs a major overhaul, so please don't rely on details. I hope we can refactor all this info when FLTK 1.5 development has started...)

... If moving to pure C is not possible, and staying put is not reasonable, then a path toward modern C++ is the only conclusion [...] then the only decision is the slope of that change. The one good thing about C++ is even if FLTK 1.5 is only up to C++11, a user can use C++17 or later in their app with no problems.

Yep, that's a good property of C++. [ Not everything of C++ is bad ;-) ]

The only issue is the interface between user code and FLTK: if FLTK is too C-like, then users need to copy strings etc. or use `c_str()` which makes user code less readable and harder to maintain. For instance, one issue is that we don't have a good way to return strings from FLTK methods/functions (and transfer ownership).

I hope that this will be better in the future (FLTK 1.5 or later).

 
I struggled whether to weigh in but figured you could ignore me.

Ignoring anybody is not our style. Although this is a group/forum targeted at the development of FLTK we appreciate comments from FLTK users as well, particularly if these comments are as useful and on point as yours. Knowing what our users think and do in their development efforts is essential.


This is a very important discussion and I'm glad to see it continuing.

... and so am I. Thanks for your contributions.

Greg Ercolano

unread,
Mar 3, 2025, 1:35:32 PMMar 3
to fltkc...@googlegroups.com

On 3/3/25 08:24, 'Albrecht Schlosser' via fltk.coredev wrote:

Is there code you can (i.e. are allowed to) post that gets the compiler name and version for us as a starter so we don't need to reinvent the wheel? Even if you are using closed source, this is maybe not an issue for such "simple macro stuff". Contributions are always appreciated (and thanks again for your previous post). Thanks in advance.

But if not, never mind, this shouldn't be too difficult.


    Perhaps an easy thing to do would be to make a separate test program project makes a few dummy classes that exercises the std stuff we want to do, and then add macros for the different compilers, and do some tests across different compilers to verify what we think we know.

    This way if anything comes up it's easy to identify, exercise, and retool, without getting into all of FLTK's innards.

Mohammed

unread,
Mar 3, 2025, 1:46:41 PMMar 3
to fltkc...@googlegroups.com
@Albrecht

It’s true the Rust bindings depend on a C library cfltk. cfltk wraps the public FLTK api in mostly function calls. The internals of cfltk however use C++17, constexpr, templates, inheritance etc. None of that is exposed externally since Rust doesn’t really support calling C++ code directly. On the Rust side, the inheritance hierarchy is mimicked using trait inheritance (the equivalent of C++ abstract classes or C#/java interfaces). Rust also allows what’s called a deref pattern, which is as close one gets to C++ structural inheritance. This is done in the Rust side as using the C interface directly doesn’t allow for extending FLTK types.

Albrecht Schlosser

unread,
Mar 3, 2025, 2:22:56 PMMar 3
to fltkc...@googlegroups.com
@Mohammed

Thanks for your explanation, and I apologize for misspelling your name
in my previous post. I knew better, it was a typo. :-(

Albrecht Schlosser

unread,
Mar 3, 2025, 2:40:45 PMMar 3
to fltkc...@googlegroups.com
On 3/3/25 18:31 Rob McDonald wrote:
I recently went down the path of trying to get a MacOS program to link with some parts built with gcc and other parts built with clang / xcode.  If both compilation units use std::, it really isn't possible.  Fundamentally, this is a libc++ vs libstdc++ problem.

Although there are hacks to gcc to point at the system SDK (libc++), it doesn't work in real life.  The upstream gcc project does not support libc++, so they have no problem breaking things without warning.  When Homebrew (or some other project) comes along with a fix, they have to fight an uphill battle to get the changes (for an unsupported platform) upstreamed.

In practice, using std:: will limit us to using one compiler per platform.  If this is not OK, then some sort of MWE test project should be constructed before major changes to FLTK are undertaken.  This test project should then be used to exercise all the toolchain combinatorics that are targeted to see if it is even possible.

Thanks for all your comments.


I am not an FLTK dev -- just a user.  So I don't really think I deserve a vote here.

Not a formal vote, yes, if it comes to a decision by voting, but...


However, I do have a few thoughts I'd like to share....

Thoughts of users are always appreciated, as I just wrote in another comment. We need to know what our user base needs.


I absolutely abhor 'auto'.  Some claim it makes code more readable -- in fact it does the opposite.  Declaring types is fundamentally documentation.  You're documenting (for future developers) exactly what type a variable has -- what information does it contain?  what methods and fields are available?  it it const or not?  etc.

When you use 'auto', you're either saying either:
1) I'm too lazy to document the type here. or
2) I'm not clever enough to figure out the type here.

Both are bad.  Unlike other documentation, declaring types has the advantage that it can not go out of date (as correctness is enforced at compile time).

I agree in most points. However, I think that sensible use of 'auto' can be helpful in *some* cases:

(1) Generally I suggest to use 'auto' only in limited cases (short code blocks) where it is (a) obvious what the type is and/or (b) avoids redundancy. Why would you want to write (from a real code example in FLTK):
  Fl_SVG_File_Surface *surface = new Fl_SVG_File_Surface(ww, wh, svg);

Using Fl_SVG_File_Surface twice is redundant, makes the line longer and harder to read, and finally it's also error prone. Why the latter, you may think? Well, copy-and-paste bugs etc. happen.

(2) Iterator syntax is complicated and not intuitive. When an object is used only in a 'for' loop that's controlled by an iterator, why bother to write the exact type of the iterator/object?

(3) In short example programs syntax like in (1) for Fl_Window, Fl_Box, and other widgets can also be abbreviated, e.g. from our test/hello.cxx, what's wrong (bad) with this modified code?
int main(int argc, char **argv) {
  // Fl_Window *window = new Fl_Window(340, 180);
  auto window = new Fl_Window(340, 180);
  auto box = new Fl_Box(20, 40, 300, 100, "Hello, World!");
  box->box(FL_UP_BOX);
  window->end();
  window->show(argc, argv);
  return Fl::run();
}

OK, some prefer to write 'auto *' in this case, but that's IMHO a matter of taste. However it's "easy" to change 'new Fl_Window' to 'new Fl_Double_Window' and to forget to change the variable type. In this case it doesn't matter (bad example), but there are cases where it matters (compilation errors would be the best that can happen).

However, I don't advocate declaring lots and lots of variables as 'auto'. This is like the discussion whether 'goto' should be used or not. There *are* cases where it's useful!

Note: this is my personal opinion and I don't want to start a discussion...


I am sympathetic to Greg and other developers who must support users on antiquated platforms.  Some of my users finally were forced to update from RHEL7 and it was a big step forward.  However, choosing to maintain support for deprecated operating systems is potentially a large burden on FLTK's team.  In my experience 'many tools' choose to limit support to the OS versions that are still supported (receiving security updates) from the vendor.  This trickles down (compilers, Homebrew, Github Actions, etc) such that supporting deprecated platforms may limit our ability or willingness to use continuous integration tools.

De facto we can't use continuous integration tools if the development platform (in our case: GitHub and in parts GitLab) doesn't provide tools for old platforms. Our resources don't allow to build our own CI farms. FYI: recently I got a mail from GitHub that 'ubuntu-20' will no longer be supported after some time, hence we can't use CI builds on ubuntu-20 in the future (I replaced this with ubuntu-latest).


I'm not saying that FLTK should not run on old iron.  Old programs on old platforms can continue to use FLTK 1.4, 1.3 or 1.1.  I simply think we should think hard about the Venn diagram of Win7 / OSX10.4 users who need to run the latest version of FLTK 1.5 apps.  It even seems possible that Apple will stop supporting Intel processors before FLTK 1.5 is finished and released.

With Apple everything seems to be possible. For us as library developers there must be a distinction:

(1) Platforms that are officially supported. On these platforms we can run CI tools and FLTK developers can develop and test on these platforms.

(2) On other platforms FLTK *may* (still) work but this is not guaranteed - i.e. these platforms are not "officially supported".

All Windows versions older than Windows 10 (or older than Windows 11, starting in Oct. 2025 when Microsoft drops Windows 10 support) are basically in category (2).

My first thought about old Windows releases (XP, 2000, ...) was that we should remove dynamic loading of newer DLL's (and workarounds if not found) for such old systems - which would result in FLTK not being able to run on old platforms. But the discussion showed that we should leave this option in FLTK so Windows 7 can still be used. However, we should (IMHO) clearly document that systems older than Windows 10/11 are in category (2), i.e. "unsupported but may work".

On macOS it's even harder: I'm updating my only MacBook Air from time to time, hence I don't have older macOS versions than currently 15.3.1. Manolo has, AFAICT, at least older SDK's so he can test building against these but I don't know what macOS versions he has. Other FLTK devs may have some older macOS systems, Intel and/or M1-M4...


In my experience, libraries that expose their template system to the user are almost always 'header only'.  This effectively eliminates any potential incompatibility with pre-compiled components of any sort.  This also effectively forces static linking for said library.

Good point.

But (sorry for nitpicking) technically you can compile and link a header-only library in a static or shared library (e.g. nanosvg) and use that in your program.


A header-only FLTK would result in smaller binaries vs. static linking -- because only the components used by an app would get built and incorporated.

In FLTK this has always been a goal for static linking. Modules are separated (or split) in a way that the linker can link only used code (mostly). Or did I misunderstand your point?


FLTK might actually be faster and lighter that way.

I doubt that this is correct. The memory footprint could be smaller if your previous assumption was true, and that could reduce the loading time. On memory constrained systems paging could be less, but effectively I don't think that a header-only library could have advantages as opposed to static linking (as in FLTK).


However, it would ditch any / all benefits of dynamic linking on platforms that use it.

In my experience, dynamic linking of FLTK "at scale" is a myth anyway.
1) if you ship your own *.dll or *.so dynamic libraries with your app, you might as well be statically linking.
2) platforms that provide system shared libraries (i.e. Linux) are hopelessly out of date in what they package
3) platforms that provide system shared libraries (Linux distributions) are hopelessly incompatible with one another

The advantage of dynamic linking is IMHO visible on systems where lots (hundreds) of identical (or just FLTK) applications are running at the same time. Then read-only parts of the shared library may be loaded only once into memory, and read-write parts are only copied (copy-on-write) if/when they are modified. This can save lots of memory.

This is all moot if you deploy your specific application to users that run this application once per client ("PC"). Then it doesn't matter much whether you use static or dynamic linking. In this case dynamic linking can even use more memory than static linking because the full shared library gets loaded, but what does it matter for a single-use program?


Thanks for all your work,

Welcome, and thanks for your comments.

Greg Ercolano

unread,
Mar 3, 2025, 2:41:10 PMMar 3
to fltkc...@googlegroups.com


On 3/3/25 10:35, Greg Ercolano wrote:

[rewritten after having a cup of coffee, for better comprehension]:
Perhaps the easy thing to do would be to make a separate test project that makes a few dummy classes to exercise the std stuff we want to use, and then add macros for the different compilers, and do some tests across different compilers to verify what we think we know.

This way if anything comes up it's easy to identify, exercise, and retool, without getting into all of the overhead of  FLTKs own code just to exercise a few simple std use cases.


    And of course adding the compiler version# macro tests to handle the compiler testing.
    We could focus on one to start with, e.g. gcc/clang on linux, do some tests with that for results.

    Also, the test program would of course let one build the test suite as a library, then
    separately a test program to use the lib that could be compiled with other compiler versions.

    I could perhaps start such a project in git and open it up to the devs, if that'd help.

Albrecht Schlosser

unread,
Mar 3, 2025, 3:11:14 PMMar 3
to fltkc...@googlegroups.com
On 3/3/25 20:41 Greg Ercolano wrote:
On 3/3/25 10:35, Greg Ercolano wrote:

[rewritten after having a cup of coffee, for better comprehension]:
Perhaps the easy thing to do would be to make a separate test project that makes a few dummy classes to exercise the std stuff we want to use, and then add macros for the different compilers, and do some tests across different compilers to verify what we think we know.


I confess that I don't fully get what you suggest (above). What macros? Particularly "do some tests across different compilers to verify what we think we know" would likely be a time consuming practice that would involve several (if not all) FLTK devs to test on "different compilers". How many different compilers can you test with on a particular platform?


This way if anything comes up it's easy to identify, exercise, and retool, without getting into all of the overhead of  FLTKs own code just to exercise a few simple std use cases.


??


    And of course adding the compiler version# macro tests to handle the compiler testing.
    We could focus on one to start with, e.g. gcc/clang on linux, do some tests with that for results.

In my mind a first step could be to use CMake to get the compiler ID and maybe version (I don't know how specific CMake can be with that). Other info can be retrieved by predefined constants such as __cplusplus in a real (compiled) program and the library itself.

This info would be displayed (again, this is only a first step) in our demo 'test/fltk-versions'. As a bonus we'd have two different values for whatever info we display:

(a) the version compiled into the test program
(b) the version queried from the library by a function call.

These versions can be different if the test program is linked against a shared FLTK library.

I'm more or less regularly building FLTK with gcc and clang, and I could build and use shared libraries and executable programs with both compilers to test interaction with each other, just as you suggested (below).


    Also, the test program would of course let one build the test suite as a library, then
    separately a test program to use the lib that could be compiled with other compiler versions.

Interesting.


    I could perhaps start such a project in git and open it up to the devs, if that'd help.

+1

I take it you mean in a separate repository?

I don't see how this could do any harm, but we should discuss what we need to know, what to test, and so on (to avoid unnecessary work).

Albrecht Schlosser

unread,
Mar 3, 2025, 3:50:27 PMMar 3
to fltkc...@googlegroups.com
On 3/3/25 21:11 'Albrecht Schlosser' via fltk.coredev wrote:
In my mind a first step could be to use CMake to get the compiler ID and maybe version (I don't know how specific CMake can be with that).

Using CMake (no executable program) for different compilers (gcc, clang, mingw-w64-gcc cross-compiler):
-- C_COMPILER_ID            GNU
-- C_COMPILER_AR            /usr/bin/gcc-ar-12
-- C_COMPILER_RANLIB        /usr/bin/gcc-ranlib-12

-- CXX_COMPILER_ID          GNU
-- CXX_COMPILER_AR          /usr/bin/gcc-ar-12
-- CXX_COMPILER_RANLIB      /usr/bin/gcc-ranlib-12


-- C_COMPILER_ID            Clang
-- C_COMPILER_AR            /usr/bin/llvm-ar-14
-- C_COMPILER_FRONTEND_VARIANT GNU
-- C_COMPILER_RANLIB        /usr/bin/llvm-ranlib-14

-- CXX_COMPILER_ID          Clang
-- CXX_COMPILER_AR          /usr/bin/llvm-ar-14
-- CXX_COMPILER_FRONTEND_VARIANT GNU
-- CXX_COMPILER_RANLIB      /usr/bin/llvm-ranlib-14


-- C_COMPILER_ID            GNU
-- C_COMPILER_AR            /usr/bin/x86_64-w64-mingw32-gcc-ar
-- C_COMPILER_RANLIB        /usr/bin/x86_64-w64-mingw32-gcc-ranlib

-- CXX_COMPILER_ID          GNU
-- CXX_COMPILER_AR          /usr/bin/x86_64-w64-mingw32-gcc-ar
-- CXX_COMPILER_RANLIB      /usr/bin/x86_64-w64-mingw32-gcc-ranlib

These are all the CMake variables I could find that are not empty strings for specific CMAKE_<LANG>_* variables on Debian 12 / Bookworm.

Don't ask me why these variables have that specific content.

Just FYI, I'm not going to test further today.

Greg Ercolano

unread,
Mar 3, 2025, 3:53:50 PMMar 3
to fltkc...@googlegroups.com


On 3/3/25 12:11, 'Albrecht Schlosser' via fltk.coredev wrote:
On 3/3/25 20:41 Greg Ercolano wrote:
On 3/3/25 10:35, Greg Ercolano wrote:

[rewritten after having a cup of coffee, for better comprehension]:
Perhaps the easy thing to do would be to make a separate test project that makes a few dummy classes to exercise the std stuff we want to use, and then add macros for the different compilers, and do some tests across different compilers to verify what we think we know.


I confess that I don't fully get what you suggest (above). What macros?

    What I've been pitching in my last few posts is to have the FLTK library build access the compiler's version#s, e.g. in the case of gcc/g++:

__GNUC__
__GNUC_MINOR__
__GNUC_PATCHLEVEL__


    ..and "bake" those into our public .H files with some "standard" FLTK name, like FL_CPP_VERSION_XXX, where XXX is MAJOR, MINOR, PATCH or some such, whatever we come up with.

    ..and then test for these FL_CPP_VERSION_XXX macro names when user applications build against our library, and compare them to the gnu compiler's own.

    Anything that doesn't match should toss a #warning message, alerting the user they're using a different compiler than the one the FLTK library was built with.


Particularly "do some tests across different compilers to verify what we think we know" would likely be a time consuming practice that would involve several (if not all) FLTK devs to test on "different compilers".

    Right, though we're trying to determine if we can use std or not, so we'd want to test this against a few compilers, maybe, just to see what the behavior/error messages are?


How many different compilers can you test with on a particular platform?

    I think you brought up gcc vs clang.

    And of course on windows, VS verses mingw and whatever else we support.
    On Mac, different versions of Xcode across different versions of OSX. I think we may have already encountered some of that with, what was it, "Mac Ports" builds of FLTK vs whatever local compiler the person installing ports used.




This way if anything comes up it's easy to identify, exercise, and retool, without getting into all of the overhead of  FLTKs own code just to exercise a few simple std use cases.


??

    OK, if the test program reveals that even with guards in place for testing compiler versions, we still get bad behavior (link errors, crashes, leaks etc) from apps compiled against slightly different FLTK prebuilt libs, we know what users might encounter before we fully deploy FLTK changes.

    Basically dip our toe in the water initially using a simple test suite before retooling FLTK's mainline.



In my mind a first step could be to use CMake to get the compiler ID and maybe version (I don't know how specific CMake can be with that). Other info can be retrieved by predefined constants such as __cplusplus in a real (compiled) program and the library itself.

    Right, by whatever means we can record the compiler version info, even if it means a shell script that parses the output of e.g. 'clang --version'.

    For the test suite we wouldn't even need to dive into cmake, so we can easily test changes, and then when the tests are all good, go for tweaking FLTK's current git.



This info would be displayed (again, this is only a first step) in our demo 'test/fltk-versions'. As a bonus we'd have two different values for whatever info we display:

(a) the version compiled into the test program
(b) the version queried from the library by a function call.

These versions can be different if the test program is linked against a shared FLTK library.

I'm more or less regularly building FLTK with gcc and clang, and I could build and use shared libraries and executable programs with both compilers to test interaction with each other, just as you suggested (below).

    Also, the test program would of course let one build the test suite as a library, then
    separately a test program to use the lib that could be compiled with other compiler versions.

Interesting.


    Ya, that's the point - to make it easy to test:

        o See if we can detect library vs. application compiler versions and throw reasonable warning errors
        o If std in the library can work properly within major/minor/patch compiler versions across all compilers we support
        o Easy to quickly build/retest without all the noise from FLTK building across different compilers

    For that last item, I could see if we test lots of compilers, esp on old machines where FLTK build times might be slow, we might have to weed through lots of compiler warnings unrelated to what we're testing for, and knowing FLTK devs, we'd get side tracked trying to solve those, instead of focusing on the std/compiler version issues.

    Just seems to make sense to peel this off as a simple test suite first, so we can exercise possible std weirdness in the API, as obviously "there be dragons".



    I could perhaps start such a project in git and open it up to the devs, if that'd help.

+1


    Assuming the above sounds OK, I can try to put something together.
    Not with cmake though, lol, just simple Makefiles. Might have to cheat on windows.

    I'll start small with just a std::string and std::vector of ints and strings.
    I'm not sure exactly how to craft code that trips up the compilers, but once I have a skeleton
    in place, the devs can play with it.

I take it you mean in a separate repository?

    Ya, small test suite separate from FLTK, just to test std stuff used in a library context, and compiler version macro testing.

    Will hold until the above makes sense, and if there's any requests for specific stuff for the test suite.

Greg Ercolano

unread,
Mar 3, 2025, 4:16:29 PMMar 3
to fltkc...@googlegroups.com

On 3/3/25 12:53, Greg Ercolano wrote:

I take it you mean in a separate repository?

    Ya, small test suite separate from FLTK, just to test std stuff used
    in a library context, and compiler version macro testing.

    And just to emphasize: not only a separate repo, but a /non-fltk/ project;
    small and simple.

    Basically a mini-library with a dummy class or two that exercises what we want;
    instancing a std string and array, give access through the API in various ways,
    and a simple and separate test application that exercises that mini-lib through its api/abi.

    The test application could be built both with the lib and separately against
    the pre-built lib using another compiler or on another machine of the same OS.

    Such a thing could be easily tested through a debugger, or modified to generate
    assembly if necessary to see what is actually going on. (I've had to do that from time to time
    to track down compiler weirdness)

Rob McDonald

unread,
Mar 3, 2025, 4:45:34 PMMar 3
to fltk.coredev
On Monday, March 3, 2025 at 11:40:45 AM UTC-8 Albrecht-S wrote:
I absolutely abhor 'auto'.  Some claim it makes code more readable -- in fact it does the opposite.  Declaring types is fundamentally documentation.  You're documenting (for future developers) exactly what type a variable has -- what information does it contain?  what methods and fields are available?  it it const or not?  etc.

When you use 'auto', you're either saying either:
1) I'm too lazy to document the type here. or
2) I'm not clever enough to figure out the type here.

Both are bad.  Unlike other documentation, declaring types has the advantage that it can not go out of date (as correctness is enforced at compile time).

I agree in most points. However, I think that sensible use of 'auto' can be helpful in *some* cases:

(1) Generally I suggest to use 'auto' only in limited cases (short code blocks) where it is (a) obvious what the type is and/or (b) avoids redundancy. Why would you want to write (from a real code example in FLTK):
Fl_SVG_File_Surface *surface = new Fl_SVG_File_Surface(ww, wh, svg);
Using Fl_SVG_File_Surface twice is redundant, makes the line longer and harder to read, and finally it's also error prone. Why the latter, you may think? Well, copy-and-paste bugs etc. happen.


What is obvious to you might not be obvious to the next person.

The copy/past errors will generally be caught at compile time.

If I want to use my IDE's ability to search for all declarations of a certain type, 'auto' defeats that.
 
(2) Iterator syntax is complicated and not intuitive. When an object is used only in a 'for' loop that's controlled by an iterator, why bother to write the exact type of the iterator/object?

I think this complexity and non-intuitiveness is exactly why it is a benefit to be explicit.  If something changes from a const iterator to an iterator, I want to know that -- not have it hidden away behind auto.  If I come along later and need to modify the for-loop, I think it is important for me to know what kind of iterator it is using.


With Apple everything seems to be possible. For us as library developers there must be a distinction:

(1) Platforms that are officially supported. On these platforms we can run CI tools and FLTK developers can develop and test on these platforms.

(2) On other platforms FLTK *may* (still) work but this is not guaranteed - i.e. these platforms are not "officially supported".

I'm OK with this distinction, but I think it leaves out the question of whether un-official platforms still get a vote or veto in terms of toolchain, language support, or other processes.

For example, MacOS 10.4 appears to only support up to XCode 2.5 (according to https://xcodereleases.com/ and https://en.wikipedia.org/wiki/Xcode ).  That is old enough that XCode was still based on gcc and not clang.  It appears that XCode 2.5 was based on gcc 3.3 or 4.0, with 4.0 as the default.  GCC 4.0 does not even support C++11 (https://gcc.gnu.org/projects/cxx-status.html).

So, drawing the line of supported platforms isn't just about 'maybe it will work' because of some subtle issue or general bit rot.  It is also about drawing some distinct lines in what will and won't work.

 
In my experience, libraries that expose their template system to the user are almost always 'header only'.  This effectively eliminates any potential incompatibility with pre-compiled components of any sort.  This also effectively forces static linking for said library.

Good point.

But (sorry for nitpicking) technically you can compile and link a header-only library in a static or shared library (e.g. nanosvg) and use that in your program.

Sure -- as long as the templates aren't exposed through the API.  If there are a finite number of possible choices (say just numeric types for a math library) then you could reasonably pre-compile for all reasonable choices.  However, that doesn't make sense in a lot of cases (say a container library).  And even for a math library, someone will want to use an infinite precision library, someone else will want to use double-doubles or quad doubles, and another user will want to use automatic differentiation implemented through operator overloading.

Even if FLTK moved to a header only design, I would not expect it to expose many templates to the API.  Perhaps eventually, but certainly not up front.

 
I doubt that this is correct. The memory footprint could be smaller if your previous assumption was true, and that could reduce the loading time. On memory constrained systems paging could be less, but effectively I don't think that a header-only library could have advantages as opposed to static linking (as in FLTK).
 
I learn something every day.  I was under the impression that everything in the binary static library file (*.a or *.lib or whatever) ended up in the final binary executable file.  Whereas, in a header-only build, the compiler could do a more fine-grained selection of what to compile and incorporate.

 
However, it would ditch any / all benefits of dynamic linking on platforms that use it.

In my experience, dynamic linking of FLTK "at scale" is a myth anyway.
1) if you ship your own *.dll or *.so dynamic libraries with your app, you might as well be statically linking.
2) platforms that provide system shared libraries (i.e. Linux) are hopelessly out of date in what they package
3) platforms that provide system shared libraries (Linux distributions) are hopelessly incompatible with one another

The advantage of dynamic linking is IMHO visible on systems where lots (hundreds) of identical (or just FLTK) applications are running at the same time. Then read-only parts of the shared library may be loaded only once into memory, and read-write parts are only copied (copy-on-write) if/when they are modified. This can save lots of memory.

That is an interesting use case I had not considered.  It is a little bit more of a stretch to imagine that application running hundreds of copies on a single machine is an interactive GUI application, but I guess it is possible.

In many situations with multiple copies of the same interactive GUI application running, you're going to want each one to be in a separate sandbox / container as well.

How does FLTK separate the read-only parts from the read-write parts?  Even if the actual separation is handled by the compiler/linker/OS, this seems to be something like data structure memory alignment -- where design decisions in the code implicitly determine what is possible much later on.  Are there enough read-only parts that this savings is still realized in practice?

Rob

Greg Ercolano

unread,
Mar 3, 2025, 4:59:58 PMMar 3
to fltkc...@googlegroups.com

On 3/3/25 13:45, Rob McDonald wrote:

That is an interesting use case I had not considered.  It is a little bit more of a stretch to imagine that application running hundreds of copies on a single machine is an interactive GUI application, but I guess it is possible.

    We've had a few users who were eyeballing the FLTK lib size very carefully.
    One balked when the library binary doubled in size (IIRC) due to some simple change we didn't realize, and we were able to retool to get that back down.

    In one case I think there was an embedded project that was trying to keep the OS and FLTK apps as small as possible, due to disk image limitations.

    I think tiny core linux is another such project, in that case to also keep the disk image small, and where many FLTK apps are the default OS interactive tools, e.g. system preferences, cpu monitor, clock, simple text editor, etc, leveraging FLTK's small footprint via dynamic libs.

    I think FLTK should really cater to such uses as much as possible, as these are aligned with one of our core tenets ("light weight").



Albrecht Schlosser

unread,
Mar 3, 2025, 6:05:15 PMMar 3
to fltkc...@googlegroups.com
On 3/3/25 21:53 Greg Ercolano wrote:
    What I've been pitching in my last few posts is to have the FLTK library build access the compiler's version#s, e.g. in the case of gcc/g++:

__GNUC__
__GNUC_MINOR__
__GNUC_PATCHLEVEL__


    ..and "bake" those into our public .H files with some "standard" FLTK name, like FL_CPP_VERSION_XXX, where XXX is MAJOR, MINOR, PATCH or some such, whatever we come up with.

    ..and then test for these FL_CPP_VERSION_XXX macro names when user applications build against our library, and compare them to the gnu compiler's own.

    Anything that doesn't match should toss a #warning message, alerting the user they're using a different compiler than the one the FLTK library was built with.


OK, understood. In the final product this would likely use a combination of CMake and compiler macros, but for now we can use whatever does the job.


Particularly "do some tests across different compilers to verify what we think we know" would likely be a time consuming practice that would involve several (if not all) FLTK devs to test on "different compilers".

    Right, though we're trying to determine if we can use std or not, so we'd want to test this against a few compilers, maybe, just to see what the behavior/error messages are?

How many different compilers can you test with on a particular platform?

    I think you brought up gcc vs clang.

    And of course on windows, VS verses mingw and whatever else we support.

Oh, yeah, ISTR that VS can also use clang now (or something like that), and MinGW / MSYS have several "flavors" of gcc and clang as well. That would be a bunch of different compiler tools, and the number of possible combinations would become "gigantic".


    On Mac, different versions of Xcode across different versions of OSX. I think we may have already encountered some of that with, what was it, "Mac Ports" builds of FLTK vs whatever local compiler the person installing ports used.

Mac Ports, Homebrew, Fink, macOS native (Xcode). Also a lot.


In my mind a first step could be to use CMake to get the compiler ID and maybe version (I don't know how specific CMake can be with that). Other info can be retrieved by predefined constants such as __cplusplus in a real (compiled) program and the library itself.

    Right, by whatever means we can record the compiler version info, even if it means a shell script that parses the output of e.g. 'clang --version'.

    For the test suite we wouldn't even need to dive into cmake, so we can easily test changes, and then when the tests are all good, go for tweaking FLTK's current git.

Well, for me using CMake would be easier than building a Makefile, particularly if the test tool is intended to be cross-platform - which it is all about.

    Just seems to make sense to peel this off as a simple test suite first, so we can exercise possible std weirdness in the API, as obviously "there be dragons".

...

    Assuming the above sounds OK, I can try to put something together.
    Not with cmake though, lol, just simple Makefiles. Might have to cheat on windows.


As I wrote above, a CMake file for a simple project is IMHO easier to create in a cross-platform way than a Makefile, but feel free to do what is easier for you.


    Ya, small test suite separate from FLTK, just to test std stuff used in a library context, and compiler version macro testing.

    Will hold until the above makes sense, and if there's any requests for specific stuff for the test suite.

I'm not sure about what we need to test. The problem with this kind of testing is that you may not expect a particular issue, hence you can't build a test case for it. I wouldn't have expected the issues described earlier with Windows DLL decorations (FL_EXPORT and whatever it's called by MS).

One thing you could start with is a class like FL_EXPORT Fl_Group that contains a vector of (widget) pointers. But this is already implemented in my fork as mentioned previously. Did it issue warnings or fail building? I don't think so but I need to check.

I'd appreciate if you proposed something we can use for testing. TIA.

Albrecht Schlosser

unread,
Mar 3, 2025, 7:29:52 PMMar 3
to fltkc...@googlegroups.com
On 3/3/25 22:45 Rob McDonald wrote:
On Monday, March 3, 2025 at 11:40:45 AM UTC-8 Albrecht-S wrote:

I agree in most points. However, I think that sensible use of 'auto' can be helpful in *some* cases:

(1) Generally I suggest to use 'auto' only in limited cases (short code blocks) where it is (a) obvious what the type is and/or (b) avoids redundancy. Why would you want to write (from a real code example in FLTK):
Fl_SVG_File_Surface *surface = new Fl_SVG_File_Surface(ww, wh, svg);
Using Fl_SVG_File_Surface twice is redundant, makes the line longer and harder to read, and finally it's also error prone. Why the latter, you may think? Well, copy-and-paste bugs etc. happen.


What is obvious to you might not be obvious to the next person.

Right, point taken. But the example above should be obvious to everyone that knows at least the basics of C++. It might be different if templates come into play.


The copy/past errors will generally be caught at compile time.

Generally but not always. It's a difference if you have
  Fl_Image *img = new Fl_Image(); // assume this was the previous code
vs.
  Fl_Image *img = new Fl_Shared_Image();  // changed RHS: 'new Fl_Image()' to 'new Fl_Shared_Image()'

when the type of the variable should have been changed as well. The compiler can't catch this error.


If I want to use my IDE's ability to search for all declarations of a certain type, 'auto' defeats that.

Good point, I agree.

More points elided, coming to another point:


For us as library developers there must be a distinction:

(1) Platforms that are officially supported. On these platforms we can run CI tools and FLTK developers can develop and test on these platforms.

(2) On other platforms FLTK *may* (still) work but this is not guaranteed - i.e. these platforms are not "officially supported".

I'm OK with this distinction, but I think it leaves out the question of whether un-official platforms still get a vote or veto in terms of toolchain, language support, or other processes.

The point here is that we can't tell if a particular platform we can't test is able to build and/or run FLTK programs. What shall we do? There may be users that try to build FLTK on Win7 but if the FLTK team doesn't have Win7 systems then we can't document whether it works or not. And if we could today ("user x says, it works") the next change tomorrow could break it.

Note: I recently managed to execute FLTK apps (test programs) on a VM with Windows XP. I'm pretty sure it was FLTK 1.4 (before release) and it was very likely cross-compiled on my Linux box. I don't have an adequate build system on this XP VM.


For example, MacOS 10.4 appears to only support up to XCode 2.5 (according to https://xcodereleases.com/ and https://en.wikipedia.org/wiki/Xcode ).  That is old enough that XCode was still based on gcc and not clang.  It appears that XCode 2.5 was based on gcc 3.3 or 4.0, with 4.0 as the default.  GCC 4.0 does not even support C++11 (https://gcc.gnu.org/projects/cxx-status.html).

In such a case it should be obvious that FLTK can't be compiled on that box because it doesn't provide a C++11 compiler (this requirement must be documented), and if GCC 4 or Xcode 2.5 don't provide C++11 compatibility, then users can decide themselves that it doesn't work. However, if you can cross-compile on another machine for this architecture then it may be possible to run FLTK executables on macOS 10.4 (if our platform support goes back to 10.4 which I can't confirm).


So, drawing the line of supported platforms isn't just about 'maybe it will work' because of some subtle issue or general bit rot.  It is also about drawing some distinct lines in what will and won't work.

As I wrote above, we can only specify requirements, we can't tell if a particular platform fulfills these requirements or not if we can't test on this platform, and as library developers we can't afford the time to investigate compiler features of any platform a user might want to use.


In my experience, libraries that expose their template system to the user are almost always 'header only'.  This effectively eliminates any potential incompatibility with pre-compiled components of any sort.  This also effectively forces static linking for said library.

Good point.

But (sorry for nitpicking) technically you can compile and link a header-only library in a static or shared library (e.g. nanosvg) and use that in your program.

Sure -- as long as the templates aren't exposed through the API.

Obviously here's a difference whether a header-only library uses templates or not. My example (nanosvg) does not. Sorry, I can't go into further details about pre-compiling templates etc. (I don't have enough experience with such stuff to be helpful).

...  I was under the impression that everything in the binary static library file (*.a or *.lib or whatever) ended up in the final binary executable file.

No, that's not correct. A static library consists typically of several modules (compilation units) that offer several "symbols" (functions, data, ...). WRT FLTK every C or C++ file is such a compilation unit. The linker picks everything to satisfy all undefined symbols. That's why order of libraries on the linker command line can be significant, and why you may need to list a library twice (if there are circular deps, e.g. libA, libB, libA).


Whereas, in a header-only build, the compiler could do a more fine-grained selection of what to compile and incorporate.

Talking about "classic" (e.g. C libraries) I think it's the opposite. For instance for 'nanosvg' you define a macro (NANOSVG_IMPLEMENTATION) before you include the header. This defines whether the code is compiled or not. Otherwise only the declarations are read, like in a classic header file.

AFAICT templates are different. IIRC the compiler compiles only the code that is needed and creates "weak" symbols so the linker "knows" how to include the same code (function, class) only once, even if the same header is included in several compilation units (and the same templates are used). I assume in such a case a template's code (i.e. a specific variant) is only instantiated if it is used in a particular compilation unit. Or something like that.


The advantage of dynamic linking is IMHO visible on systems where lots (hundreds) of identical (or just FLTK) applications are running at the same time. Then read-only parts of the shared library may be loaded only once into memory, and read-write parts are only copied (copy-on-write) if/when they are modified. This can save lots of memory.

That is an interesting use case I had not considered.  It is a little bit more of a stretch to imagine that application running hundreds of copies on a single machine is an interactive GUI application, but I guess it is possible.

The classic usage would be a (Unix) server that runs application code with a UI based on X11 where the X (display) servers are remote "clients". Note the unusual client/server roles.

Another newer example would be Windows terminal servers. The application logic runs on the terminal server whereas the client displays the GUI only, in my experience with RDP or VNC where the actual UI rendering is primarily done on the terminal server and the "image" is transferred to the (thin) client.


In many situations with multiple copies of the same interactive GUI application running, you're going to want each one to be in a separate sandbox / container as well.

When I was running such application code in my previous life ;-) I was using an (Open)VMS system which could serve several hundred clients. As mentioned above, this server had the application logic and FLTK clients on PC's displayed the GUI. There were no sandboxes involved (our servers ran non-stop for a year or more, until we installed a software or OS upgrade).


How does FLTK separate the read-only parts from the read-write parts?  Even if the actual separation is handled by the compiler/linker/OS,

Yes, that's how it is done. The compiler generates different "program sections" which have attributes like writeable, executable, etc. in the object code. All strings like "Hello, World" are collected in non-executable read-only sections (with appropriate protection), and all code is (or should be) in executable, read-only sections. Since all this is collected in memory pages, the OS can apply hardware protection on these read-only pages.


this seems to be something like data structure memory alignment -- where design decisions in the code implicitly determine what is possible much later on.  Are there enough read-only parts that this savings is still realized in practice?

All code and text strings and likely much more is read-only. Static data is typically copy-on-write and is only copied to private process memory when modified by the program. That's all long established features, even Windows can do this. ;-)

<OT>Maybe interesting facts: one of the main developers of VMS (in the 1970/80's) was Dave Cutler who was later hired by Microsoft and led the development of Windows NT and worked on later Windows versions, Azure, and Xbox(!).
https://en.wikipedia.org/wiki/Dave_Cutler
"David Neil Cutler Sr. ... developed several computer operating systems, namely Microsoft Windows NT, and Digital Equipment Corporation's RSX-11M, VAXELN, and VMS."

The order was the opposite though: RSX-11M, VAXELN, and VMS was before NT and other Microsoft OS's.

I worked with RSX-11M (on PDP-11) and VMS (on VAX and Alpha machines, later also on IA64) before I even started to work with Windows and Linux/Unix.
</OT>

Greg Ercolano

unread,
Mar 3, 2025, 8:31:50 PMMar 3
to fltkc...@googlegroups.com

On 3/3/25 15:05, 'Albrecht Schlosser' via fltk.coredev wrote:

One thing you could start with is a class like FL_EXPORT Fl_Group that contains a vector of (widget) pointers. But this is already implemented in my fork as mentioned previously. Did it issue warnings or fail building? I don't think so but I need to check.

I'd appreciate if you proposed something we can use for testing. TIA.


    Work in progress here; test lib + test application:
    https://github.com/erco77/fltk-std-check

    I nabbed the Fl_Export.H stuff, and will add compiler version checking as I go.

    When I have something working with compiler checks, I'll let you know.

    For now I'm going to limit myself to linux, just to keep the Makefile simple, and gives me easy access to scripting.

    I imagine once I have something generally working, you guys can take it over and refit it with cmake.

Greg Ercolano

unread,
Mar 3, 2025, 11:21:10 PMMar 3
to fltkc...@googlegroups.com

On 3/3/25 17:31, Greg Ercolano wrote:

On 3/3/25 15:05, 'Albrecht Schlosser' via fltk.coredev wrote:


I'd appreciate if you proposed something we can use for testing. TIA.


    Work in progress here; test lib + test application:
    https://github.com/erco77/fltk-std-check

    I nabbed the Fl_Export.H stuff, and will add compiler version checking as I go.

    OK, commited something that tests the compiler versions and throws a #warning.
    Currently tested on linux only with g++ (9.x) and clang (10.x) on my ubuntu 20.04 machine.

    Albrecht, give it a try; see the README for how to use.

    Might be brittle; this is a first test, it's late, just got it working. Will revisit tomorrow.

    Currently static builds only, but that's still good enough to test the compiler
    check stuff.

    I suggest we follow up in the project's git issues, as we did with Fl_Terminal
    to keep the development details noise out of the group.

imacarthur

unread,
Mar 4, 2025, 5:12:58 AMMar 4
to fltk.coredev
On Monday, 3 March 2025 at 20:53:50 UTC, Greg wrote:

__GNUC__

__GNUC_MINOR__ __GNUC_PATCHLEVEL__

So, this (checking the preproc defines) was how I imagined doing this - rather than having CMAKE or etc. figure it out at build time.

That being so, I thought I'd check a few things, to see what this actually finds: clang didn't do quite what I'd expected, TBH; not sure if this is "normal" or an artefact of my weird clang setup...

So, running: 

    "g++ -dM -E -x c++ test_macro_structs.c | grep -i gnuc "

for one of the older gcc's I have, I get:

#define __GNUC__ 6
#define __GNUC_MINOR__ 3 
#define __GNUC_PATCHLEVEL__ 0

(amongst other things, but these are the ones we probably care about...)

Running:
      clang++  -dM -E -x c++ test_macro_structs.c  | grep -i CLANG

I get:

#define __clang__ 1
#define __clang_major__ 12
#define __clang_minor__ 0
#define __clang_patchlevel__ 1

OK so far. But running

       clang++  -dM -E -x c++ test_macro_structs.c  | grep -i GNUC

I get

#define __GNUC__ 4
#define __GNUC_MINOR__ 2
#define __GNUC_PATCHLEVEL__ 1

So... um... this clang-12.0.1 also claims to be gcc-4.2.1... which is *really* pretty old. 
That can not be normal, surely? 
That must be some artefact of this weird clang setup? 
What do other folks see clang reporting?

Also, I assume for MSVC we'd just look for _MSC_VER and maybe also for _MSVC_LANG?
Though the MSDN docs seem to suggest that _MSVC_LANG is only set in more recent compilers, and even then maybe only if the language version is c++14 or later.
That said, I don't have VS on this PC so can't actually check right now.


Gonzalo Garramuño

unread,
Mar 4, 2025, 6:03:32 AMMar 4
to fltkc...@googlegroups.com


El 4/3/25 a las 07:12, imacarthur escribió:

This is what I have in mrv2:

      o << "Compiled with "
#ifdef __GLIBCXX__

          << _("With gcc ") << __GNUC__ << endl

#elif defined(__clang__)
          << _("With clang ") << __clang__ << " " << __llvm__ << endl
#else
          << _("With msvc ") << _MSC_VER << endl

#endif



--
You received this message because you are subscribed to the Google Groups "fltk.coredev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkcoredev...@googlegroups.com.

Gonzalo Garramuño

unread,
Mar 4, 2025, 6:07:54 AMMar 4
to fltkc...@googlegroups.com

I also do:


    const std::string build_info()
    {
        std::stringstream s;
        s << _("Build Environment:") << std::endl
          << _("\tDistribution: ") << kBUILD_DISTRO << " " <<
kBUILD_VERSION
          << std::endl
          << _("\tDesktop Environment: ") << kBUILD_DESKTOP_ENV <<
std::endl
          << _("\tKernel Info: ") << kBUILD_KERNEL_INFO << std::endl;
        return s.str();
    }


Which I fill in CMake with:


set(DISTRO_VERSION "")
if(UNIX AND NOT APPLE)

    execute_process(COMMAND lsb_release -ds OUTPUT_VARIABLE LSB_INFO
OUTPUT_STRIP_TRAILING_WHITESPACE)
    string(REPLACE "\"" "" DISTRO_INFO "${LSB_INFO}")

    execute_process(COMMAND ${CMAKE_COMMAND} -E env -- echo
"$ENV{XDG_SESSION_DESKTOP}" OUTPUT_VARIABLE DESKTOP_ENV
OUTPUT_STRIP_TRAILING_WHITESPACE)
    execute_process(COMMAND uname -r OUTPUT_VARIABLE KERNEL_INFO
OUTPUT_STRIP_TRAILING_WHITESPACE)

    list(APPEND LIBRARIES OpenGL::OpenGL)
    if(TLRENDER_WAYLAND)
    list(APPEND LIBRARIES OpenGL::EGL)
    endif()
elseif(WIN32)
    execute_process(
    COMMAND powershell -Command "(Get-CimInstance
Win32_OperatingSystem).Caption"
    OUTPUT_VARIABLE DISTRO_INFO
    OUTPUT_STRIP_TRAILING_WHITESPACE
    )
    set(DESKTOP_ENV "Windows (GDI+)")
    execute_process(COMMAND cmd /C ver OUTPUT_VARIABLE KERNEL_INFO
OUTPUT_STRIP_TRAILING_WHITESPACE)
elseif(APPLE)
    execute_process(COMMAND sw_vers --productName
    OUTPUT_VARIABLE DISTRO_INFO)
    execute_process(COMMAND sw_vers --productVersion
    OUTPUT_VARIABLE DISTRO_VERSION)
    set(DESKTOP_ENV "Cocoa")
    execute_process(COMMAND uname -r OUTPUT_VARIABLE KERNEL_INFO)
endif()

--
Gonzalo Garramuño
ggar...@gmail.com

Albrecht Schlosser

unread,
Mar 4, 2025, 9:17:01 AMMar 4
to fltkc...@googlegroups.com
On 3/4/25 11:12 imacarthur wrote:
On Monday, 3 March 2025 at 20:53:50 UTC, Greg wrote:

__GNUC__

__GNUC_MINOR__ __GNUC_PATCHLEVEL__

So, this (checking the preproc defines) was how I imagined doing this - rather than having CMAKE or etc. figure it out at build time.

That being so, I thought I'd check a few things, to see what this actually finds: clang didn't do quite what I'd expected, TBH; not sure if this is "normal" or an artefact of my weird clang setup...

So, running: 

    "g++ -dM -E -x c++ test_macro_structs.c | grep -i gnuc "

for one of the older gcc's I have, I get:

#define __GNUC__ 6
#define __GNUC_MINOR__ 3 
#define __GNUC_PATCHLEVEL__ 0

[...]

My results are similar, clang also claims to be (compatible with?) gcc 4.2.1:
$ touch test_macro_structs.c # create empty file

$ g++ -dM -E -x c++ test_macro_structs.c | grep GNUC | sort
#define __GNUC__ 12
#define __GNUC_MINOR__ 2
#define __GNUC_PATCHLEVEL__ 0

$ clang++ -dM -E -x c++ test_macro_structs.c  | egrep 'clang|GNUC' | sort
#define __clang__ 1
#define __clang_major__ 14
#define __clang_minor__ 0
#define __clang_patchlevel__ 6
#define __clang_version__ "14.0.6 "

#define __GNUC__ 4
#define __GNUC_MINOR__ 2
#define __GNUC_PATCHLEVEL__ 1

Note: output edited to remove irrelevant stuff.
I have a higher clang version than Ian (14.0.6) but result is also GNUC 4.2.1. I think this is just the bare minimum clang claims to be compatible with. My clang version is default from Debian 12 / Bookworm.

In practice we can check the __clang_* variables before the __GNUC_* variables (#if ... #elif ... #else ... #endif) and that should do it.

imacarthur

unread,
Mar 4, 2025, 10:01:10 AMMar 4
to fltk.coredev
On Tuesday, 4 March 2025 at 14:17:01 UTC Albrecht-S wrote:

My results are similar, clang also claims to be (compatible with?) gcc 4.2.1:
 
In practice we can check the __clang_* variables before the __GNUC_* variables (#if ... #elif ... #else ... #endif) and that should do it.

OK, so not just me then. Interesting...
FWIW, I tried an older version of clang (10.0.1) and it also reports being gcc-4.2.1 (along with the clang values of course)

But yes, if we check for " __clang__  " before we check for " __GNUC__  " we should be fine.


Albrecht Schlosser

unread,
Mar 4, 2025, 10:16:50 AMMar 4
to fltkc...@googlegroups.com
On 3/4/25 05:21 Greg Ercolano wrote:
>>
>>     Work in progress here; test lib + test application:
>> https://github.com/erco77/fltk-std-check
>>
>>     I nabbed the Fl_Export.H stuff, and will add compiler version
>> checking as I go.
>
>
>     Albrecht, give it a try; see the README for how to use.
>

Done, see open issue(s).

>     I suggest we follow up in the project's git issues, as we did with
> Fl_Terminal
>     to keep the development details noise out of the group.
>

Yep, agreed and done so. Let's follow up there...

Manolo

unread,
Mar 4, 2025, 11:09:51 AMMar 4
to fltk.coredev
Here is what I get using Xcode 16's compiler on macOS 15:

/Library/Developer/CommandLineTools/usr/bin/c++ -dM -E -x c++ -I.. fl_rect.cxx | egrep 'clang|GNUC' | sort

#define FL_CLANG_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__)
#define FL_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
#define __GNUC_GNU_INLINE__ 1

#define __GNUC_MINOR__ 2
#define __GNUC_PATCHLEVEL__ 1
#define __GNUC__ 4
#define __VERSION__ "Apple LLVM 16.0.0 (clang-1600.0.26.6)"
#define __clang__ 1
#define __clang_literal_encoding__ "UTF-8"
#define __clang_major__ 16
#define __clang_minor__ 0
#define __clang_patchlevel__ 0
#define __clang_version__ "16.0.0 (clang-1600.0.26.6)"

Reply all
Reply to author
Forward
0 new messages