The main problem here is that the implementation files (all .cpp files and the .ino sketch) are all compiled independently.
The #include directive is just a straight copy-paste of the contents of the header file into the implementation file. Together with the macros you're using and the other preprocessor directives (everything that starts with #), they are handled by the preprocessor, before the code is passed to the compiler. The compiler never sees your option macros.
This means that the .cpp file always sees the default version of your class, and it is compiled completely separately from the sketch, so it isn't affected by the options that might be defined in the sketch, it doesn't even know the sketch exists. The compiler compiles the .cpp file to an object file.
On the other hand, your .ino file only sees the version of the class with the specified options. The sketch is also compiled to a separate object file.
Then both object files are stitched together by the linker. The compiler never sees the two files together, so it has no idea that there are two different versions of your "DynamicLibrary" class.*
In conclusion, there is no way to communicate the settings in your sketch to the other .cpp files.
AFAIK, the only way is to provide a global macro definition of your options, either as a compiler flag (-DOPTIONS -DRAM), but this is almost impossible to do with the Arduino builder, or by defining them inside of a "Config.h" header file that's included in DynamicLibrary.h. That does of course make it harder for users of the library to change the optimization options, they'll have to edit a file inside of the library directory to do so.
If you know for a fact that your library will only be included in the main .ino sketch file, you could use a header only library, with options that are defined before including it (like what you're doing right now, but without the .cpp file). If you or a user includes your header-only library in one or more .cpp files, however, you end up with exactly the same problem as you have now.
(*) This isn't entirely true, since most Arduino Cores compile sketches with link-time optimizations enabled, so it does invoke the compiler at link time in this case, but even then, the compiler doesn't check whether there are different versions of you class.
giorgos_xou:
You are right. Now i see what you mean in this specific case, but i still see the potential of this method being used in some other cases. Is it really that wrong if i want to use it just like i used it, only from the skectch or should i approach it in another way?
Yes, it is really wrong. See the quote I posted earlier: the program is ill-formed, no diagnostic required. Your program is not valid if you use this method, and the compiler doesn't have to tell you that it's not valid, it just does "something", but there's no way to know what it'll do. It might generate an empty program, it might generate a program that crashes, it might generate a program that works on some architectures but not on others ...
It might work by "chance" with some compilers and options, it might suddenly break if you add or remove code, because the compiler decides to optimize your code differently, inlining some functions, etc.
You cannot rely on it, I wouldn't ever use it in a critical project or a public library.
giorgos_xou:
i i actually decide to use this method
Ηowever, one more question: if i actually decide to use this "trick" method, is it something that could be eliminated/stop-being-supported in the next versions of avr-gcc or it is safe for someone to use it as a feature?
It is simply not supported, the fact that it works now is just chance. There's a very real possibility that it'll break when the Arduino team decides to update the compiler.
giorgos_xou:
Also, how about this:
#pragma once
class DynamicLibrary {
public:
#if !defined(OPTIONS)
void Function1(); // default
void Function1(bool O1); // Optimized for SRAM
void Function1(bool O1 , bool O2); // Optimized for CPU
void do_the_default_thing();
void do_the_default_thing(bool O1);
void do_the_default_thing(bool O1 , bool O2);
#else
#if defined(RAM)
void Function1(bool O1 = true);
void do_the_default_thing(bool O1 = true);
#elif defined(CPU)
void Function1(bool O1 = true , bool O2 = true);
void do_the_default_thing(bool O1 = true, bool O2 = true);
#else
//default?
#endif
#endif
};
It still violates the C++ standard, and adds even more complexity. You simply cannot do it this way using macros, see the alternatives I mentioned above.