Is it possible to use #ifdef like checks in assembler? - cross-platform

I have tested a bit of assembler on Linux using the AT&T syntax. One thing that struck me was that the book I was reading was written from a 32-bit standpoint. Thus, all sizes would have to be changed to the correct 64-bit versions for me. Or I could (which I did) assemble the code using the --32 flag for as and the -melf_i386 flag for ld when linking. I have also adapted some of the code and to run on Windows under Cygwin.
But that got me thinking. Is there a way to do ifdef like checks in assembler to do one thing if I'm on Windows and another under Linux and also handle 32 vs 64 bit that way? For example to have a .globl _start under Linux and a .globl _main under Windows.
Or is this handled by checking before assembling and having different source files to assemble based on the result of the checks?
I.e. foo_linux.s and foo_windows.s
If so, how do you overcome that fact that you will not know which .s files you will use, and thus have to include, when you are creating your program?
For example, say that we have a socket_linux.s and a socket_windows.s. They both present an identical interface but do the OS specific work associated to sockets. But when I work with the sockets in my program I will not know if I need the Windows or Linux version included. So I would be kinda screwed :)
So how is this handled in Assembler? In C++ for example I could include my socket.h and socket.cpp and wrap all the Linux and Windows specific code in #ifdef statements.

If you use GCC to compile your files and name them .S (with uppercase S) or .sx, it will pass them through the preprocessor before invoking the assembler.
From the docs:
file.s
Assembler code.
file.S
file.sx
Assembler code which must be preprocessed.
You can add -v to the command line to see how the various sub-processes are invoked.

in MASM (.asm), you can use ifdef, ifndef and the likes, as:
ifdef X64
endif

When writing for different platforms you can define some macro for loading target specific files:
FILE target.h
#if defined(__arm__)
#define target "arm"
#elif defined(__x86_64__)
#if defined(_WIN64)
#define target "win64"
#else
#define target "linux64" // all non-Win share the same calling convention
#endif
#else
// 32bit defs
#endif
Then you can include target specific files with the macro, two string literals successively get one single literal:
#include "target.h"
#include "target_specific_code_" target ".h"
It includes one of these files:
target_specific_code_arm.h
target_specific_code_win64.h
target_specific_code_linux64.h
...
EDIT:
Like this, you can also define target specific assembler instructions for later use in inline assembly:
#ifdef ...
#define ASM_PP_LOAD_WORD "movi "
#else
#define ASM_PP_LOAD_WORD "mov "
#endif
or as macro
#ifdef ...
// when using intel assembler there is a different
// order of parameters
#define ASM_PP_LOAD_WORD(a, b) "movi " #b ", " #a
#else
#define ASM_PP_LOAD_WORD(a, b) "mov " #a ", " #b
#endif

Related

How can I define an executable that has different compiler command-line arguments?

I have a C++ project and I want to test the compatibility of library headers with different compiler versions. I have a simple source file (that includes said headers) and I want to change the compiler argument to std=gnu++11 for this one target. How do I do that?
executable('old_compiler_test', ['octest.cxx']
# override ARGS here ??? how
)
Note that I have
add_global_arguments(
['-std=gnu++17',
....
rather than the dedicated option for this, in spite of the warning to prefer the special option, because the special option simply doesn't work. (Why is a question I've never tracked down)
update
To clarify: I'm not trying to make additional configurations in the same way that debug and release are configurations. I want a different compiler argument to be applied to a single target within the configuration.
From the Meson documentation, you can use the argument <languagename>_args to pass additional compiler arguments. In your case, since you use C++, it would give something like
executable('old_compiler_test', ['octest.cxx'],
cpp_args: ['std=gnu++11']
)
However the documentation also specify that there are no way to disable an argument added by add_global_argument(), so you will end up with both -std=gnu++17 and -std=gnu++11 passed to the compiler. I don't know how your compiler will behave, but I tried to pass both arguments to GCC 10.2 and it uses c++17 (not what you want).
Workaround
It seems that if you define the C++ version in the project() statement, Meson will removes it if an other version is specified in compiler arguments, giving the behaviour you expect.
Here is the sample I used:
meson.build
project('project-name', 'cpp',
default_options: ['cpp_std=c++17']
)
executable('old_compiler_test', ['octest.cxx'],
cpp_args: ['-std=gnu++11']
)
octest.cxx
#include <iostream>
int main() {
std::cout << __cplusplus << std::endl;
}
After compilation, running the executable will print 201103, which means that the compiler used c++11 as desired.

Fortran: Is there a way to conditionally use modules?

Assume I have two Fortran modules called modA and modB. Is there a way to use one or the other in a program based on a conditional statement? Does this require some type of preprocessing? For example, I want to be able to do something like the following code:
if (condition)
use modA
else
use modB
end
I am using the GNU Fortran compiler.
Yes, you must do some kind of preprocessing. The most common is the C preprocessor included in GNU Fortran.
#if (condition)
use modA
#else
use modB
#endif
The preprocessor does not understand your Fortran code, it is only a text for it. It has it's own set of directives and it's own set of variables. Only the preprocessor variables can be used in the condition, not your Fortran variables.
Another common directive is #ifdef which is a variant of #if defined. See the manual for more https://gcc.gnu.org/onlinedocs/cpp/Traditional-Mode.html (gfortran runs the preprocessor in the traditional mode).
To enable the preprocessor use the -cpp flag or in Unix you can use capital F in the file suffix.

CMake configure_file replace ON/OFF with 1/0

Suppose I have two libraries ABC, XYZ and two variables ABC_FOUND XYZ_FOUND in CMakeLists.txt. In a config.h.in file, I have the following lines
#define __USE_ABC__ #ABC_FOUND#
#define __USE_XYZ__ #XYZ_FOUND#
If I do configure_file(config.h.in config.h), the output config.h file will be changed to
#define __USE_ABC__ ON
#define __USE_XYZ__ OFF
My question is how can I convert the ON/OFF options into 1/0, in other word, my desired output config.h should be
#define __USE_ABC__ 1
#define __USE_XYZ__ 0
Define ABC_FOUND to either 0 or 1 with
#cmakedefine01 #ABC_FOUND#
Use an intermediate variable in either environment to change it to USE_ABC.
Likely, you should use #cmakedefine instead of hard defines (see examples e.g. here)
But please remember that in the case of #cmakedefine you get a configuration header where "enabled features" (in terms of CMake if() command) are #define-d to a supplied value, while "disabled features" are #undef-ined. So you should test them not with #if <something> but rather with #ifdef <something>.

Funny font in the build messages in Codeblocks using g++-4 (Cygwin) as compiler

I am using CodeBlocks 10.05 with Cygwin 1.7 to compile some C++ codes. The operating system is WinXP SP3. The compiler used is g++ 4.5.3.
When I build the following program:
#include <stdio.h>
#include <stdlib.h>
using namespace std;
int main()
{
unsigned long long a = 12345678901234;
printf("%u\n",a);
return 0;
}
it outputs the following in the build log:
C:\Documents and Settings\Zhi Ping\Desktop\UVa\143\main.cpp||In function ‘int main()’:|
C:\Documents and Settings\Zhi Ping\Desktop\UVa\143\main.cpp|9|warning: format ‘%u’ expects type ‘unsigned int’, but argument 2 has type ‘long long unsigned int’|
C:\Documents and Settings\Zhi Ping\Desktop\UVa\143\main.cpp|9|warning: format ‘%u’ expects type ‘unsigned int’, but argument 2 has type ‘long long unsigned int’|
||=== Build finished: 0 errors, 2 warnings ===|
I do not know why CodeBlocks prints the ‘ etc. symbols. Is there a way for CodeBlocks to properly display the characters?
Cygwin defaults to the UTF-8 encoding, whereas it looks like CodeBlocks assumes that output is in CP1252. Furthermore, since Cygwin tells it that UTF-8 is available, gcc uses separate left and right versions of quote characters instead of the usual ASCII ones. The result is what you're seeing. There are two ways to tackle this: either tell CodeBlocks to use UTF-8, or tell gcc to stick to ASCII by setting LANG=C. I don't know how to do either of these in CodeBlocks though.
Add the following Environment Variable to your computer:
LANG=C
In Windows 7, you can add it by going to Computer > Properties > Advanced System Settings > Environment Variables, then "New...". The menus should be similar in Windows XP.
I hope it's ok to answer an old question. This happened to me today as well, and it took me a while to fix it.

MinGW and "declaration does not declare anything"

I'm working on converting a Linux project of mine to compile on Windows using MinGW. It compiles and runs just fine on Linux, but when I attempt to compile it with MinGW it bombs out with the following error message:
camera.h:11: error: declaration does not declare anything
camera.h:12: error: declaration does not declare anything
I'm kind of baffled why this is happening, because
I'm using the same version of g++ (4.4) on both Linux and Windows (via MinGW).
The contents of camera.h is absurdly simple.
Here's the code. It's choking on lines 11 and 12 where float near; and float far; are defined.
#include "Vector.h"
#ifndef _CAMERA_H_
#define _CAMERA_H_
class Camera{
public:
Vector eye;
Vector lookAt;
float fov;
float near;
float far;
};
#endif
Thanks for your help.
EDIT: Thanks both Dirk and mingos, that was exactly the problem!
Edit If you happen to include windef.h (either directly or indirectly), you will find
#define FAR
#define far
#define NEAR
#define near
there. I think, that this is the culprit.
Try
#undef near
#undef far
before your class definition.
Try giving them different names, like
float my_near;
float my_far;
I recall Borland using "near" and "far" as keywords (my 1992 Turbo C had these, back in MS-DOS era). Dunno if this is the case with gcc, but you can always try that.
In <windef.h>, you'll find on the following lines:
#define NEAR
#define near
Simple answer: you can't #undef them because they're a part of the Windows headers (_WINDEF_H will still be defined even if you #undef those definitions, so it won't be re-included if you try to #include <windef.h> again, not to mention the fact that if you #undef _WINDEF_H before using #include <windef.h> after your class definition, you'll end up with duplicate definitions for things like RECT, LONG, PROC and more), so the only other solution is to change your variable names.