The questions pretty simple. I want want a function (C++) or method which will, on call, returun something like
"Windows" //or
"Unix"
Nothing fancy, I dont need the version numbe or anything. Just the os name. A quick google searc didnt turn up anything useful, so I thought I'd post this here
Since you can not have a single binary file which runs over all operating systems, and you need to re-compile your code again. It's OK to use MACROs.
Use macros such as
_WIN32
_WIN64
__unix
__unix__
__APPLE__
__MACH__
__linux__
__FreeBSD__
like this
std::string getOsName()
{
#ifdef _WIN32
return "Windows 32-bit";
#elif _WIN64
return "Windows 64-bit";
#elif __APPLE__ || __MACH__
return "Mac OSX";
#elif __linux__
return "Linux";
#elif __FreeBSD__
return "FreeBSD";
#elif __unix || __unix__
return "Unix";
#else
return "Other";
#endif
}
You should read compiler's manuals and see what MACROS they provided to detect the OS on compile time.
From the Poco source code:
Win32:
std::string EnvironmentImpl::osNameImpl()
{
OSVERSIONINFO vi;
vi.dwOSVersionInfoSize = sizeof(vi);
if (GetVersionEx(&vi) == 0) throw SystemException("Cannot get OS version information");
switch (vi.dwPlatformId)
{
case VER_PLATFORM_WIN32s:
return "Windows 3.x";
case VER_PLATFORM_WIN32_WINDOWS:
return vi.dwMinorVersion == 0 ? "Windows 95" : "Windows 98";
case VER_PLATFORM_WIN32_NT:
return "Windows NT";
default:
return "Unknown";
}
}
Unix:
std::string EnvironmentImpl::osNameImpl()
{
struct utsname uts;
uname(&uts);
return uts.sysname;
}
Related
I'm trying to run the expression _setmode only if I'm using Windows, and setlocale only if I'm using Linux, but I can't manage to make them work with a simple if-else inside a function due to Linux having errors with the Windows libraries, and vice versa.
#if defined(_WIN32) || defined(_WIN64) || (defined(__CYGWIN__) && !defined(_WIN32))
#define PLATFORM_NAME 0
#include <io.h>
#include <fcntl.h>
#elif defined(__linux__)
#define PLATFORM_NAME 1
#include <locale>
#elif defined(__APPLE__) && defined(__MACH__)
#include <TargetConditionals.h>
#if TARGET_OS_MAC == 1
#define PLATFORM_NAME 2
#endif
#else
#define PLATFORM_NAME NULL
#endif
#if PLATFORM_NAME == 0
_setmode(_fileno(stdout), _O_U8TEXT);
#endif
#if PLATFORM_NAME == 1
setlocale(LC_CTYPE, "");
#endif
If you write OS-dependent* code (as in this case), you can manage it at compile-time**. To do this, we need two parts:
Define OS-dependent constants (optional, if condition simple, this part can be omitted):
#if defined(_WIN32)
#define PLATFORM_NAME 0
#include <fcntl.h>
#include <io.h>
#elif defined(__linux__)
#define PLATFORM_NAME 1
#include <locale>
#endif
In needed place call OS-dependent code with preprocessor conditions:
#if PLATFORM_NAME == 0
//Windows code here
#endif
You can write more complex conditions:
#if PLATFORM_NAME == 0
//Windows code here
#elif PLATFORM_NAME != 0
//Non-Windows code here
#if PLATFORM_NAME == 1 || PLATFORM_NAME == 2
//Linux or unknown OS code here
#endif
#endif
See restrictions of conditions here
Tip: If your code have entrypoint (main function as example), you can call most of OS-dependent code at main if this help to reduce code. In library you can place OS-dependent code to dedicated source-file functions like there. Usage preprocessor-time code is good method for writing zero-cost runtime code because of preprocessor just remove all sources,if they're not met conditions.
* - or whatever-dependent 😃
** - more precisely, preprocessor-time
Source: GNU, Microsoft docs.
I am writing a code for macOS application.
The application would be running on M1 based Macs as well as Intel based Macs also.
What would be the switch to differentiate M1 and Intel?
if (M1)
{
do something for M1
}
else if (Intel)
{
do something for Intel
}
I think, you can use __arm__ to detect arm architecture:
#ifdef __arm__
//do smth on arm (M1)
#else
//do smth on x86 (Intel)
#endif
I was just fooling around with this and found this reference for Objective-C from apple that seemed to work with clang for C++.
// Objective-C example
#include "TargetConditionals.h"
#if TARGET_OS_OSX
// Put CPU-independent macOS code here.
#if TARGET_CPU_ARM64
// Put 64-bit Apple silicon macOS code here.
#elif TARGET_CPU_X86_64
// Put 64-bit Intel macOS code here.
#endif
#elif TARGET_OS_MACCATALYST
// Put Mac Catalyst-specific code here.
#elif TARGET_OS_IOS
// Put iOS-specific code here.
#endif
https://developer.apple.com/documentation/apple-silicon/building-a-universal-macos-binary
I specifically checked to see if TARGET_CPU_ARM64 was defined in my header.
Hopefully this helps someone.
If you need a runtime check instead of compile time check, you can think of using something like below
#include <sys/sysctl.h>
#include <mach/machine.h>
int main(int argc, const char * argv[])
{
cpu_type_t type;
size_t size = sizeof(type);
sysctlbyname("hw.cputype", &type, &size, NULL, 0);
int procTranslated;
size = sizeof(procTranslated);
// Checks whether process is translated by Rosetta
sysctlbyname("sysctl.proc_translated", &procTranslated, &size, NULL, 0);
// Removes CPU_ARCH_ABI64 or CPU_ARCH_ABI64_32 encoded with the Type
cpu_type_t typeWithABIInfoRemoved = type & ~CPU_ARCH_MASK;
if (typeWithABIInfoRemoved == CPU_TYPE_X86)
{
if (procTranslated == 1)
{
cout << "ARM Processor (Running x86 application in Rosetta)";
}
else
{
cout << "Intel Processor";
}
}
else if (typeWithABIInfoRemoved == CPU_TYPE_ARM)
{
cout << "ARM Processor";
}
}
I wrote below code to detect OS and it is working fine with boost version > 1.55 but older boost library ( e.g. 1.48 ) do not support operating system macros.
Below is the sample code that works fine with boost >= 1.55.
std::string GetOSPlatform()
{
std::string platformStr = "Unknown";
#if defined(BOOST_OS_MACOS) || defined(BOOST_OS_IOS)
platformStr = "osx";
#endif
#if defined(BOOST_OS_WINDOWS)
platformStr = "windows";
#if BOOST_ARCH_X86_64
platformStr += "-x64";
#endif
#endif
#if defined(BOOST_OS_UNIX) || defined(BOOST_OS_LINUX)
platformStr = "linux";
#if BOOST_ARCH_X86_64
platformStr += "-x64";
#endif
#endif
#if BOOST_OS_SOLARIS
platformStr = "solaris";
#if BOOST_ARCH_SPARC
platformStr += "-sparc";
#else
platformStr += "-x64";
#endif
#endif
#if BOOST_OS_HPUX
platformStr = "hp-ux";
#endif
return platformStr;
}
int main()
{
std::string pltform = GetOSPlatform();
std::cout << "platform....." << pltform << std::endl;
return 0;
}
Here "BOOST_OS_*" macros are not supported with boost version < 1.55 so how to replace above code so that it works with older boost version as well ?
Any suggestions?
In order to get the code to work, you add some conditional compilation based on the version of Boost that you are running. If it's 1.55 or later, you just compile the code as it is. If it's an earlier version, you simply reproduce the OS detection code that later Boost versions use locally, e.g. by simply copying the code from a later version.
I need to get a C-string, which contains fs name.
There are a lot of commands to print fs name in terminal, but I can not find easy way to get it in C/C++ code.
You parse /proc/mounts.
If you have used one of the stat() family of functions, and have a filesystem identifier (st_dev) in numerical form, you simply stat() the mounted directory at each mount point listed in /proc/mounts (append /./ to each mount point, so that you stat the mounted directory, and not the mount point in its parent filesystem), until you see one that matches. Using that entry (line) you obtain the type of the filesystem, as the kernel sees it.
Remember that /proc/ and /sys/ in Linux systems are not on disk, but are the proper interface for the kernel to expose certain details. Currently mounted filesystems (in /proc/mounts) is one of these.
In posixc, this is very simple to implement using fopen(), getline(), fclose(), free(), and strtok() or sscanf() or your own line-splitting function. Remember that as a kernel interface, the contents of the files in /proc/ and /sys/ are never localized; they are always in the default C/POSIX locale.
Linux C API has statfs() (inspired by BSD, for other unix like OS have look into stat from coreutils).
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/vfs.h>
// https://github.com/linux-test-project/ltp/tree/master/include/tst_fs.h
/* man 2 statfs or kernel-source/include/linux/magic.h */
#define TST_BTRFS_MAGIC 0x9123683E
#define TST_NFS_MAGIC 0x6969
#define TST_RAMFS_MAGIC 0x858458f6
#define TST_TMPFS_MAGIC 0x01021994
#define TST_V9FS_MAGIC 0x01021997
#define TST_XFS_MAGIC 0x58465342
#define TST_EXT2_OLD_MAGIC 0xEF51
/* ext2, ext3, ext4 have the same magic number */
#define TST_EXT234_MAGIC 0xEF53
#define TST_MINIX_MAGIC 0x137F
#define TST_MINIX_MAGIC2 0x138F
#define TST_MINIX2_MAGIC 0x2468
#define TST_MINIX2_MAGIC2 0x2478
#define TST_MINIX3_MAGIC 0x4D5A
#define TST_UDF_MAGIC 0x15013346
#define TST_SYSV2_MAGIC 0x012FF7B6
#define TST_SYSV4_MAGIC 0x012FF7B5
#define TST_UFS_MAGIC 0x00011954
#define TST_UFS2_MAGIC 0x19540119
#define TST_F2FS_MAGIC 0xF2F52010
#define TST_NILFS_MAGIC 0x3434
#define TST_EXOFS_MAGIC 0x5DF5
#define TST_OVERLAYFS_MAGIC 0x794c7630
#define TST_FUSE_MAGIC 0x65735546
// https://github.com/linux-test-project/ltp/tree/master/lib/tst_fs_type.c
const char *tst_fs_type_name(long f_type)
{
switch (f_type) {
case TST_TMPFS_MAGIC:
return "tmpfs";
case TST_NFS_MAGIC:
return "nfs";
case TST_V9FS_MAGIC:
return "9p";
case TST_RAMFS_MAGIC:
return "ramfs";
case TST_BTRFS_MAGIC:
return "btrfs";
case TST_XFS_MAGIC:
return "xfs";
case TST_EXT2_OLD_MAGIC:
return "ext2";
case TST_EXT234_MAGIC:
return "ext2/ext3/ext4";
case TST_MINIX_MAGIC:
case TST_MINIX_MAGIC2:
case TST_MINIX2_MAGIC:
case TST_MINIX2_MAGIC2:
case TST_MINIX3_MAGIC:
return "minix";
case TST_UDF_MAGIC:
return "udf";
case TST_SYSV2_MAGIC:
case TST_SYSV4_MAGIC:
return "sysv";
case TST_UFS_MAGIC:
case TST_UFS2_MAGIC:
return "ufs";
case TST_F2FS_MAGIC:
return "f2fs";
case TST_NILFS_MAGIC:
return "nilfs";
case TST_EXOFS_MAGIC:
return "exofs";
case TST_OVERLAYFS_MAGIC:
return "overlayfs";
case TST_FUSE_MAGIC:
return "fuse";
default:
return "unknown";
}
}
void print_filesystem(const char* path)
{
if (path == NULL)
return;
struct statfs s;
if (statfs(path, &s)) {
fprintf(stderr, "statfs(%s) failed: %s\n", path, strerror(errno));
return;
}
printf("'%s' filesystem: %s\n", path, tst_fs_type_name(s.f_type));
}
int main(int argc, char *argv[]) {
print_filesystem("/");
print_filesystem("/tmp");
return 0;
}
Example:
$ gcc -Wall filesystem.c -o filesystem && ./filesystem
'/' filesystem: ext2/ext3/ext4
'/tmp' filesystem: tmpfs
I'm working on an application that uses an custom, platform-dependent logger. The application defines some printf-style macros:
#define LOG_DEBUG(format, ...) \
logger.log(DEBUG, __FUNCTION__, format, ##__VA_ARGS__)
...
The past few days I've been working on moving the application to use boost.log. The biggest problem I'm having is trying to retain this macro format so that only the logger internals need to be changed, since boost's logging API is implemented in iostream-style, i.e.
BOOST_LOG(logger) << "something";
Is there an easy way to provide a macro that takes printf-style args and prints them to the boost logger without having to use string buffers? (I would think that having to format to a string would be a performance impact)
If not, is there a way to format a string using va_args without having to #ifdef for different platform implementations of formatting functions? (this was the whole point of moving to boost.log in the first place)
Or just use Boost Format to deal with the printf format string. For example,
template<typename Level, typename T1>
inline void log_format(
const Level level,
const char* const fmt,
const T1& t1)
{
BOOST_LOG_SEV(logger::get(), level) << boost::format(fmt) % t1;
}
Creating logger and extending this to handle multiple arguments (most probably with variadic template arguments) is left as an exercise for the reader.
Unfortunately, there are no clean implementations without the #ifdef statement. I know you want to port your existing logging to boost log as-is. That would be an incorrect way of doing things. printf was designed to be used for C, while boost log was designed for C++. So, my suggestion would be to use it the right way. You can make your logging macros painless than what you've shown in your code sample.
Here's my implementation of boost log where instead of having BOOST_LOG(logger) << "something";, I have it as roLOG_INFO << "something";. I believe this example comes from one of boost log's samples.
RoLog.h
#include <boost/logging/format_fwd.hpp>
#include <boost/logging/writer/on_dedicated_thread.hpp>
// Optimize : use a cache string, to make formatting the message faster
BOOST_LOG_FORMAT_MSG( optimize::cache_string_one_str<> )
#ifndef BOOST_LOG_COMPILE_FAST
#include <boost/logging/format.hpp>
#include <boost/logging/writer/ts_write.hpp>
#endif
// Specify your logging class(es)
typedef boost::logging::logger_format_write< > log_type;
// Declare which filters and loggers you'll use
BOOST_DECLARE_LOG_FILTER(roLogFilter, boost::logging::level::holder)
BOOST_DECLARE_LOG(roLog, log_type)
#define roLOG_WHERE_EACH_LINE 0
#if defined(roLOG_WHERE_EACH_LINE) && roLOG_WHERE_EACH_LINE
# define _roLOG_WHERE << roWHERE
#else
# define _roLOG_WHERE
#endif
// Define the macros through which you'll log
#define roLOG_DBG BOOST_LOG_USE_LOG_IF_LEVEL(roLog(), roLogFilter(), debug ) << "[ DEBUG ]:" _roLOG_WHERE
#define roLOG_WARN BOOST_LOG_USE_LOG_IF_LEVEL(roLog(), roLogFilter(), warning) << "[ WARNING ]:" _roLOG_WHERE
#define roLOG_ERR BOOST_LOG_USE_LOG_IF_LEVEL(roLog(), roLogFilter(), error ) << "[ ERROR ]:" _roLOG_WHERE
#define roLOG_CRIT BOOST_LOG_USE_LOG_IF_LEVEL(roLog(), roLogFilter(), fatal ) << "[ CRITICAL]:" _roLOG_WHERE
#define roLOG_INFO BOOST_LOG_USE_LOG_IF_LEVEL(roLog(), roLogFilter(), info )
struct RoLogOptions;
void roInitLogs(const RoLogOptions& options);
RoLog.cpp
#include <Core/RoLog.h>
#include <Core/RoLogOptions.h>
#include <boost/logging/format.hpp>
#include <boost/logging/writer/ts_write.hpp>
using namespace boost::logging;
BOOST_DEFINE_LOG(roLog, log_type)
BOOST_DEFINE_LOG_FILTER(roLogFilter, level::holder)
#define BLOCK(stmts) do{stmts}while(false)
#define CHECK_AND_DO(var,stmt) if (var) stmt
//------------------------------------------------------------------------------
void roInitLogs(const RoLogOptions& options)
{
static bool initialize = true;
if (initialize)
{
// Add formatters and destinations
// That is, how the message is to be formatted...
CHECK_AND_DO(options.printIndex, roLog()->writer().add_formatter(formatter::idx()));
CHECK_AND_DO(options.printTimestamp, roLog()->writer().add_formatter(formatter::time(options.timestampFormat)));
CHECK_AND_DO(options.autoAppendLine, roLog()->writer().add_formatter(formatter::append_newline()));
// ... and where should it be written to
roLog()->writer().add_destination(destination::dbg_window());
CHECK_AND_DO(options.printToStdOut, roLog()->writer().add_destination(destination::cout()));
if (!options.logFile.empty())
{
destination::file_settings settings;
settings.do_append(options.logFileAppendExisting);
roLog()->writer().add_destination(destination::file(options.logFile, settings));
}
CHECK_AND_DO(options.turnOffCache, roLog()->turn_cache_off());
// ... and set the log-level
level::type boost_log_level;
switch(options.logLevel)
{
case eLogLevel_None:
boost_log_level = level::disable_all;
break;
case eLogLevel_All:
boost_log_level = level::enable_all;
break;
case eLogLevel_Debug:
boost_log_level = level::debug;
break;
case eLogLevel_Info:
boost_log_level = level::info;
break;
case eLogLevel_Warning:
boost_log_level = level::warning;
case eLogLevel_Error:
boost_log_level = level::error;
break;
case eLogLevel_Critical:
boost_log_level = level::fatal;
break;
case eLogLevel_Default:
default:
# ifdef _DEBUG
boost_log_level = level::debug;
# else
boost_log_level = level::info;
# endif // _DEBUG
break;
};
roLogFilter()->set_enabled(boost_log_level);
initialize = false;
}
}
DISCLAIMER: I'm not claiming this to be a perfect piece of code. It works for me and I'm happy with it.
Let's say you would still like the option of logging the printf style, then consider the following code:
class LogUtil
{
static void VLogError(const char* format, va_list argList)
{
#ifdef _WIN32
int32 size = _vscprintf(format, argList) + 1;
#else
int32 size = vsnprintf(0, 0, format, argList) + 1;
#endif
if (size > 0)
{
boost::scoped_array<char> formattedString(new char[size]);
vsnprintf(formattedString.get(), size, format, argList);
roLOG_ERR << formattedString.get();
}
}
}
#define roLOGF_ERR(format, ...) LogUtil::VLogError(format, ##__VA_ARGS)
DISCLAIMER: I haven't tested the above code. You might need to tweak it to make it work for you.