compile time function exists check

3,984 views
Skip to first unread message

earo...@gmail.com

unread,
Feb 2, 2015, 9:33:01 AM2/2/15
to std-pr...@isocpp.org
Has someone proposed something like this to alleviate the current spaghetti of autoconf :

#ifndef(char *mkdtemp(char *))
    char *mkdtemp(char *) {
      ....
    }
#endif

I assume this has been proposed, but haven't been able to find it.

Jean-Marc Bourguet

unread,
Feb 2, 2015, 9:38:19 AM2/2/15
to std-pr...@isocpp.org, earo...@gmail.com
Not that I know of.

It doesn't fit in the current compilation phases, so that doesn't seem easy to define, nor to implement.

Yours,

David Krauss

unread,
Feb 3, 2015, 2:58:31 AM2/3/15
to std-pr...@isocpp.org
On 2015–02–02, at 10:33 PM, earo...@gmail.com wrote:

I assume this has been proposed, but haven't been able to find it.

Disabling function declarations is the purview of Concepts, so this might be possible:

template< typename, typename = void > bool is_mkdtemp_defined = false;
template< typename t > bool is_mkdtemp_defined
    < t, decltype(void(mkdtemp( t{} ))) > = true;

char *mkdtemp( char * ) requires( ! is_mkdtemp_defined< char * > ) {

Templates can refer to undeclared function names. I’m not sure whether requires clauses are allowed in non-template contexts, but such an extension should be pretty straightforward.

If it’s OK for mkdtemp to be defined as a template (if you don’t need ABI compatibility with C code), then you can already do:

template< typename, typename = void >
struct is_mkdtemp_defined : std::false_type {};
template< typename t > struct is_mkdtemp_defined
    < t, decltype(void(mkdtemp( t{} ))) > : std::true_type {};

template< typename t = char *,
    typename = std::enable_if_t< ! is_mkdtemp_defined< t >::value > >
char * mkdtemp( char * ) { 

Actually, this would still produce a C-compatible library if extern "C" were allowed in templates, but it’s not (§14/4).

See also http://stackoverflow.com/q/7313919/153285 . There’s also a static_if proposal, but that medicine is worse than the disease.

David Krauss

unread,
Feb 3, 2015, 3:06:53 AM2/3/15
to std-pr...@isocpp.org

On 2015–02–03, at 3:58 PM, David Krauss <pot...@gmail.com> wrote:

template< typename, typename = void >
struct is_mkdtemp_defined : std::false_type {};
template< typename t > struct is_mkdtemp_defined
    < t, decltype(void(mkdtemp( t{} ))) > : std::true_type {};

template< typename t = char *,
    typename = std::enable_if_t< ! is_mkdtemp_defined< t >::value > >
char * mkdtemp( char * ) { 

Ehhh, this violates the ODR because the template definition changes meaning between the first point of instantiation and the end of the source file. More future-proof solutions would use a namespace and a fallback overload as in the linked StackOverflow page.

Erik Aronesty

unread,
Feb 3, 2015, 8:55:18 AM2/3/15
to Jean-Marc Bourguet, std-pr...@isocpp.org
It would certainly be a compile-time, not a precompiler time call.   At compile-time it would not be very difficult, since compilers are already checking for the existence of ambiguous symbols.

But you'd need some proper construct for it.

Probably the easiest way would be a reserved word, akin to the "throws" construct.   No big change to the parse/lexer, easy to remember.

char * mkdtemp(char *) conditional {
      ....
}

This would be akin to the "weak" linker construct... but would be more powerful and reliable, since the symbol would not even be exported.


Currently, all symbol declarations are "unconditional".   "Conditional symbol declarations" is a fairly critical feature that would vastly increase productivity when developing in C/C++

Again, though, it should probably be a C feature... not limited to C++.


Erik Aronesty

unread,
Feb 3, 2015, 9:05:40 AM2/3/15
to Jean-Marc Bourguet, std-pr...@isocpp.org
I would imagine that the only allowed conditions would be the existence or non-existence of various symbols, classes, members, etc.   This would enable the production of clean, cross-platform code.



Thiago Macieira

unread,
Feb 3, 2015, 2:17:35 PM2/3/15
to std-pr...@isocpp.org
On Tuesday 03 February 2015 08:55:17 Erik Aronesty wrote:
> It would certainly be a compile-time, not a precompiler time call. At
> compile-time it would not be very difficult, since compilers are *already
> checking *for the existence of ambiguous symbols.
>
> But you'd need some proper construct for it.
>
> Probably the easiest way would be a reserved word, akin to the "throws"
> construct. No big change to the parse/lexer, easy to remember.
>
> char * mkdtemp(char *) conditional {
> ....
> }
>
> This would be akin to the "weak" linker construct... but would be more
> powerful and reliable, since the symbol would not even be exported.
>
>
> Currently, all symbol declarations are "unconditional". "Conditional
> symbol declarations" is a fairly critical feature that would vastly
> increase productivity when developing in C/C++
>
> Again, though, it should probably be a C feature... not limited to C++.

This exists in ELF and Mach-O and is supported by GCC, Clang and ICC.

For example, from Qt's Android support:

#ifdef Q_OS_ANDROID
// Android lacks pthread_condattr_setclock, but it does have a nice function
// for relative waits. Use weakref so we can determine at runtime whether it
is
// present.
static int local_cond_timedwait_relative(pthread_cond_t*, pthread_mutex_t *,
const timespec *)
__attribute__((weakref("__pthread_cond_timedwait_relative")));
#endif

[...]

#ifdef Q_OS_ANDROID
if (Q_LIKELY(local_cond_timedwait_relative)) {
ti.tv_sec = time / 1000;
ti.tv_nsec = time % 1000 * Q_UINT64_C(1000) * 1000;
return local_cond_timedwait_relative(&cond, &mutex, &ti);
}
#endif
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358

earo...@gmail.com

unread,
Feb 11, 2015, 11:33:55 AM2/11/15
to std-pr...@isocpp.org
Yeah, just like weak ... but weak attributes are link-time, and are, therefore inherently dangerous and won't be easy to make work with lots of compilers.   And they're nonstandard, nonportable.

I really hate the idea that C++11 is coming out without a standard for portability improvements.  

Heck even standardizing on "weakref", as much as it is lacking, would be far better than standardizing on nothing.

David Krauss

unread,
Feb 12, 2015, 9:12:46 AM2/12/15
to std-pr...@isocpp.org

On 2015–02–12, at 12:33 AM, earo...@gmail.com wrote:

Yeah, just like weak ... but weak attributes are link-time, and are, therefore inherently dangerous and won't be easy to make work with lots of compilers.   And they're nonstandard, nonportable.

I really hate the idea that C++11 is coming out without a standard for portability improvements.  

Heck even standardizing on "weakref", as much as it is lacking, would be far better than standardizing on nothing.

Certain committee members are allergic to discussion of linkage, but it is a current practice, well within the ISO mandate of standardization.

Also, a basic form of weak linkage is already in the standard in the form of replacement operator new. The case in this thread has replacement/weakness going the other way, but perhaps the concept of replacement could still be adapted to apply.

Zijie He

unread,
Feb 13, 2015, 12:42:05 AM2/13/15
to std-pr...@isocpp.org
Do I understand totally wrongly to the question? You want to have a function, which will call another function if existing, otherwise provide a new implementation.
Then the following code may help,
#pragma once
#include <type_traits>
#include "../utils/macro.hpp"

namespace __type_traits_private
{
    class Yes_t;
    class No_t;

    template <typename T>
    struct yes
    {
        const static bool value = (std::is_same<T, Yes_t&>::value) ||
                                  (std::is_same<T, Yes_t>::value);
    };

    template <typename T>
    struct no
    {
        const static bool value = (std::is_same<T, No_t&>::value) ||
                                  (std::is_same<T, No_t>::value);
    };
}

#ifdef __PRIVATE_HAS_DEF
    __PRIVATE_HAS_DEF redefined
#endif
#define __PRIVATE_HAS_DEF(name, TYPENAMES, BEGIN, type_check_typenames, type_check_parameters) \
            template <TYPENAMES> \
            struct name { \
                BEGIN; \
                template <type_check_typenames> \
                struct type_check; \
                template <typename U> \
                static __type_traits_private::Yes_t& test(type_check<type_check_parameters>*); \
                template <typename> \
                static __type_traits_private::No_t& test(...); \
                const static bool value = __type_traits_private::yes<decltype(test<T>(nullptr))>::value; };

#ifdef HAS_TYPEDEF
    HAS_TYPEDEF redefined
#endif
#define HAS_TYPEDEF(typedef_name) __PRIVATE_HAS_DEF(has_typedef_##typedef_name, \
                                                    typename T, \
                                                    , \
                                                    typename U, \
                                                    U::typedef_name);

#ifdef HAS_MEMBER_FUNCTION
    HAS_MEMBER_FUNCTION redefined
#endif
#define HAS_MEMBER_FUNCTION(func) __PRIVATE_HAS_DEF(has_member_function_##func, \
                                                    typename T COMMA typename RET_T COMMA typename... Args, \
                                                    typedef RET_T (T::*Sign)(Args&&...), \
                                                    typename U COMMA U, \
                                                    Sign COMMA &U::func);

#ifdef HAS_CONST_MEMBER_FUNCTION
    HAS_CONST_MEMBER_FUNCTION redefined
#endif
#define HAS_CONST_MEMBER_FUNCTION(func) __PRIVATE_HAS_DEF(has_const_member_function_##func, \
                                                          typename T COMMA typename RET_T COMMA typename... Args, \
                                                          typedef RET_T (T::*Sign)(Args&&...) const, \
                                                          typename U COMMA U, \
                                                          Sign COMMA &U::func);

It's pretty hard to understand, but you just need to use it in such way,
        struct copy_convert_to
    {
        template <typename T1, typename T2>
        static bool copy(const T1& i, T2& o)
        {
            return i.convert_to(o);
        }
    };

    struct copy_failure
    {
        template <typename T1, typename T2>
        static bool copy(const T1& i, T2& o)
        {
            return false;
        }
    };

    struct class_convertor
    {
    private:
        HAS_CONST_MEMBER_FUNCTION(convert_to);

    public:
        template <typename T1, typename T2>
        static bool copy(const T1& i, T2& o)
        {
            typedef typename std::conditional<
                                 has_const_member_function_convert_to<T1, bool, T2&>::value,
                                 copy_convert_to,
                                 copy_failure
                             >::type
                             copy_type;
            return copy_type::copy(i, o);
        }
    };

But personally I do not like this implementation, it can only check a full defined type before the line involved. Class declaration forward will break the detection, since they are pretty compiling time logic.

Erik Aronesty

unread,
Feb 13, 2015, 11:20:37 AM2/13/15
to std-pr...@isocpp.org
It's a compile time issue, not a runtime issue, and the purpose is to set standards for compile-time pragmas that enable portability.   Template hacks are unreadable, and unlikely to be used/useful.

And it should work without templates, so you can provide definitions for top level library functions.

The purpose is if an operating system doesn't provide a facility, like mkdtemp, you can write a "conditional" function which is used only if not already defined.

If you have ever looked at "portable" C++ code, you would realize, very quickly, that this is an urgent, unmet need.

(( This is what "weak" was for... but "weak" isn't a standard, and it's a link-time attribute, which, by definition, cannot be standardized.))

Another nice feature for portablility would be to standardizing preprocessor include-guards IE: when including file X  you *always* get some sort of INCLUDED_X defined or NOT_INCLUDED_X pre-defined for you.   Guessing names that change from library version to library version is standard practice now, and it's horrible and the purpose of standards bodies is to prevent this.  Or you can make a #ifinclude(<lib.h>) or something.



--

---
You received this message because you are subscribed to a topic in the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/std-proposals/o9Jj23M3a3U/unsubscribe.
To unsubscribe from this group and all its topics, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

Thiago Macieira

unread,
Feb 13, 2015, 12:52:55 PM2/13/15
to std-pr...@isocpp.org
On Friday 13 February 2015 11:20:35 Erik Aronesty wrote:
> It's a compile time issue, not a runtime issue, and the purpose is to set
> standards for compile-time pragmas that enable portability. Template
> hacks are unreadable, and unlikely to be used/useful.
>
> And it should work without templates, so you can provide definitions for
> top level library functions.
>
> The purpose is if an operating system doesn't provide a facility, like
> mkdtemp, you can write a "conditional" function which is
> *used only if not already defined.*
> If you have ever looked at "portable" C++ code, you would realize, very
> quickly, that this is an urgent, unmet need.

I totally agree.

I've managed to get away with some hacks, like for example for accept4:

namespace { namespace Supplement {
int accept4(...) { errno = ENOSYS; return -1; }
} }

fd = ::accept4(s, addr, static_cast<QT_SOCKLEN_T *>(addrlen), sockflags);
if (fd != -1 || !(errno == ENOSYS || errno == EINVAL))
return fd;
// fall back

> (( This is what "weak" was for... but "weak" isn't a standard, and it's a
> link-time attribute, which, by definition, cannot be standardized.))

It depends on having C interfaces because you need to know the external name.
That would never work for C++ code.

> Another nice feature for portablility would be to standardizing
> preprocessor include-guards IE: when including file X you *always* get
> some sort of INCLUDED_X defined or NOT_INCLUDED_X pre-defined for you.
> Guessing names that change from library version to library version is
> standard practice now, and it's horrible and the purpose of standards
> bodies is to prevent this. Or you can make a #ifinclude(<lib.h>) or
> something.

I think we can use the SD-6 recommendation and make it a stronger requirement:

#if __has_include(<lib.h>)

For Qt, we decided that SD-6 is mandatory for C++14 features. When GCC 5 is
more widespread, we'll probably start relying on __has_include too for certain
features.

Zijie He

unread,
Mar 2, 2015, 7:16:05 PM3/2/15
to std-pr...@isocpp.org
I totally agree that, it will be better to have a compiler internal functionality to check whether a function is existing during compiling. But the template I have provided strictly follow the standard, and the same magic uses widely in boost :)

Jim Porter

unread,
Mar 2, 2015, 7:48:32 PM3/2/15
to std-pr...@isocpp.org
On 2/13/2015 10:20 AM, Erik Aronesty wrote:
> It's a compile time issue, not a runtime issue, and the purpose is to
> set standards for compile-time pragmas that enable portability.
> Template hacks are unreadable, and unlikely to be used/useful.
[snip]
> If you have ever looked at "portable" C++ code, you would realize, very
> quickly, that this is an urgent, unmet need.

I disagree rather strongly. I don't believe this is the responsibility
of a compiler, but rather the responsibility of a build configuration
system. While the preprocessor is very fast, it's even faster to do this
*before* compilation, i.e. during the "configure" step of your build.
This way, incremental builds don't even need to perform this check. Not
only that, this allows for more complex configuration tests such as
ensuring that the semantics of a function match your expectations (e.g.
the XSI vs GNU versions of strerror_r). Perhaps in this example, the
differing return type (int vs char*) would be enough for your proposal
to detect the semantic difference, but this doesn't necessarily apply
universally.

There are still other problems, such as Windows' tendency to prefix C
runtime functions with "_", e.g. _isatty(). This is semantically the
same as isatty(), and an intelligent configuration system could abstract
out this difference for you (e.g. by automatically providing an isatty()
shim on Windows in some project-specific namespace), but I don't see how
a standard language feature could be expected to do this, especially
given that POSIX isatty() and Windows _isatty() are found in different
headers (unistd.h vs io.h).

Even the proposed (and already-implemented) __has_include directive is
somewhat suspect. While it can be nice for simple things, like picking
between std::experimental::any and boost::any, this is again a
configuration-time issue, not a compile-time issue. Worse, it doesn't
actually ensure that boost::any matches your expected interface: Boost
1.54 and below don't have boost::any::clear(), whereas the Library
Extensions proposal (and Boost 1.55+) do. Besides that, the boilerplate
to check this is a bit excessive, and I think it would make more sense
as autogenerated code. At that point, you could just unconditionally
#include the appropriate header (and alias the appropriate namespace) in
a generated-at-config-time header file.

I agree of course that better cross-platform configuration tools should
exist for C++, but I don't think they belong in the language itself. The
build configuration system* is a better place.

- Jim

* e.g. autotools, CMake, etc. Granted, build config systems are usually
painful to use, but that's an argument for a better build config system,
not for incorporating these features into the compiler.

Jim Porter

unread,
Mar 2, 2015, 7:56:35 PM3/2/15
to std-pr...@isocpp.org
On 3/2/2015 6:48 PM, Jim Porter wrote:
> I disagree rather strongly. I don't believe this is the responsibility
> of a compiler, but rather the responsibility of a build configuration
> system. While the preprocessor is very fast, it's even faster to do this
> *before* compilation, i.e. during the "configure" step of your build.

Correction: this wouldn't happen in the preprocessing phase, but I
expect for many cases, it would still be fairly speedy.

In any case, I'd be happy to talk off-list about build configuration
systems, since I've been toying with some ideas about how they can be
improved.

- Jim

Reply all
Reply to author
Forward
0 new messages