Skip to content

[WIP] Add better C support #135

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
87 changes: 86 additions & 1 deletion include/cppast/compile_config.hpp
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@

namespace cppast
{
/// The C++ standard that should be used.
/// The C/C++ standard that should be used.
enum class cpp_standard
{
cpp_98,
@@ -26,8 +26,14 @@ enum class cpp_standard
cpp_17,
cpp_2a,
cpp_20,
c_89,
c_99,
c_11,
c_17,
c_2x,

cpp_latest = cpp_standard::cpp_14, //< The latest supported C++ standard.
c_latest = cpp_standard::c_17, //< The latest supported C standard.
};

/// \returns A human readable string representing the option,
@@ -52,12 +58,82 @@ inline const char* to_string(cpp_standard standard) noexcept
return "c++2a";
case cpp_standard::cpp_20:
return "c++20";
case cpp_standard::c_89:
return "c89";
case cpp_standard::c_99:
return "c99";
case cpp_standard::c_11:
return "c11";
case cpp_standard::c_17:
return "c17";
case cpp_standard::c_2x:
return "c2x";
}

DEBUG_UNREACHABLE(detail::assert_handler{});
return "ups";
}

/// \returns The C/C++ standard corresponding to the string, e.g. `cpp_14` for `c++14`
/// \throws std::invalid_argument for an unknown language standard
inline cpp_standard to_standard(const std::string& str)
{
if (str == "c++98")
return cpp_standard::cpp_98;
else if (str == "c++03")
return cpp_standard::cpp_03;
else if (str == "c++11")
return cpp_standard::cpp_11;
else if (str == "c++14")
return cpp_standard::cpp_14;
else if (str == "c++1z")
return cpp_standard::cpp_1z;
else if (str == "c++17")
return cpp_standard::cpp_17;
else if (str == "c++2a")
return cpp_standard::cpp_2a;
else if (str == "c++20")
return cpp_standard::cpp_20;
else if (str == "c89")
return cpp_standard::c_89;
else if (str == "c99")
return cpp_standard::c_99;
else if (str == "c11")
return cpp_standard::c_11;
else if (str == "c17")
return cpp_standard::c_17;
else if (str == "c2x")
return cpp_standard::c_2x;
else
throw std::invalid_argument("invalid C/C++ standard '" + str + "'");
}

/// \returns whether the language standard is a C standard
inline bool is_c_standard(cpp_standard standard) noexcept
{
switch (standard)
{
case cpp_standard::cpp_98:
case cpp_standard::cpp_03:
case cpp_standard::cpp_11:
case cpp_standard::cpp_14:
case cpp_standard::cpp_1z:
case cpp_standard::cpp_17:
case cpp_standard::cpp_2a:
case cpp_standard::cpp_20:
return false;
case cpp_standard::c_89:
case cpp_standard::c_99:
case cpp_standard::c_11:
case cpp_standard::c_17:
case cpp_standard::c_2x:
return true;
}

DEBUG_UNREACHABLE(detail::assert_handler{});
return false;
}

/// Other special compilation flags.
enum class compile_flag
{
@@ -114,6 +190,12 @@ class compile_config
return do_get_name();
}

/// \returns Whether to parse files as C rather than C++.
bool use_c() const noexcept
{
return do_use_c();
}

protected:
compile_config(std::vector<std::string> def_flags) : flags_(std::move(def_flags)) {}

@@ -157,6 +239,9 @@ class compile_config
/// \notes This allows detecting mismatches of configurations and parsers.
virtual const char* do_get_name() const noexcept = 0;

/// \returns Whether to parse files as C rather than C++.
virtual bool do_use_c() const noexcept = 0;

std::vector<std::string> flags_;
};
} // namespace cppast
19 changes: 16 additions & 3 deletions include/cppast/cpp_storage_class_specifiers.hpp
Original file line number Diff line number Diff line change
@@ -19,10 +19,16 @@ enum cpp_storage_class_specifiers : int

cpp_storage_class_auto = 1, //< *automatic* storage duration.

cpp_storage_class_static = 2, //< *static* or *thread* storage duration and *internal* linkage.
cpp_storage_class_extern = 4, //< *static* or *thread* storage duration and *external* linkage.

cpp_storage_class_thread_local = 8, //< *thread* storage duration.
cpp_storage_class_register = 2, //< *automatic* storage duration.
/// Hints to the compiler to keep this in a register.
/// \notes In C a register variable cannot have its address taken. For C++ `register` is
/// deprecated in C++11 and removed in C++17.

cpp_storage_class_static = 4, //< *static* or *thread* storage duration and *internal* linkage.
cpp_storage_class_extern = 8, //< *static* or *thread* storage duration and *external* linkage.

cpp_storage_class_thread_local = 16, //< *thread* storage duration.
/// \notes This is the only one that can be combined with the others.
};

@@ -49,6 +55,13 @@ inline bool is_extern(cpp_storage_class_specifiers spec) noexcept
{
return (spec & cpp_storage_class_extern) != 0;
}

/// \returns Whether the [cppast::cpp_storage_class_specifiers]() contain `register`.
inline bool is_register(cpp_storage_class_specifiers spec) noexcept
{
return (spec & cpp_storage_class_register) != 0;
}
} // namespace cppast


#endif // CPPAST_CPP_STORAGE_CLASS_SPECIFIERS_HPP_INCLUDED
48 changes: 38 additions & 10 deletions include/cppast/cpp_type.hpp
Original file line number Diff line number Diff line change
@@ -286,25 +286,47 @@ class cpp_dependent_type final : public cpp_type
std::unique_ptr<cpp_type> dependee_;
};

/// The kinds of C++ cv qualifiers.
enum cpp_cv : int
/// Flags for the kinds of C/C++ qualifiers.
enum cpp_cv_flags : int
{
cpp_cv_none,
cpp_cv_const,
cpp_cv_volatile,
cpp_cv_const_volatile,
cpp_cv_const, //< constant type
cpp_cv_volatile, //< volatile type
cpp_cv_restrict, //< restrict pointer type (C-only)
cpp_cv_atomic, //< atomic type (C-only)

_flag_set_size, //< \exclude
};

/// Flag set for the kinds for C/C++ qualifiers.
using cpp_cv = type_safe::flag_set<cpp_cv_flags>;

/// Represents a const volatile qualified type
constexpr cpp_cv cpp_cv_const_volatile = cpp_cv(cpp_cv_const) | cpp_cv_volatile;
/// Represents an unqualified type
constexpr cpp_cv cpp_cv_none = cpp_cv();

/// \returns `true` if the qualifier contains `const`.
inline bool is_const(cpp_cv cv) noexcept
inline bool is_const(const cpp_cv& cv) noexcept
{
return cv == cpp_cv_const || cv == cpp_cv_const_volatile;
return (cv & cpp_cv_const) != 0;
}

/// \returns `true` if the qualifier contains `volatile`.
inline bool is_volatile(cpp_cv cv) noexcept
inline bool is_volatile(const cpp_cv& cv) noexcept
{
return (cv & cpp_cv_volatile) != 0;
}

/// \returns `true` if the qualifier contains `restrict`.
inline bool is_restrict(const cpp_cv& cv) noexcept
{
return cv == cpp_cv_volatile || cv == cpp_cv_const_volatile;
return (cv & cpp_cv_restrict) != 0;
}

/// \returns `true` if the qualifier contains `atomic`.
inline bool is_atomic(const cpp_cv& cv) noexcept
{
return (cv & cpp_cv_atomic) != 0;
}

/// A [cppast::cpp_cv]() qualified [cppast::cpp_type]().
@@ -355,6 +377,12 @@ const cpp_type& remove_const(const cpp_type& type) noexcept;
/// \returns The type without top-level volatile qualifiers.
const cpp_type& remove_volatile(const cpp_type& type) noexcept;

/// \returns The type without top-level restrict qualifiers.
const cpp_type& remove_restrict(const cpp_type& type) noexcept;

/// \returns The type without top-level atomic qualifiers.
const cpp_type& remove_atomic(const cpp_type& type) noexcept;

/// A pointer to a [cppast::cpp_type]().
class cpp_pointer_type final : public cpp_type
{
17 changes: 13 additions & 4 deletions include/cppast/cpp_type_alias.hpp
Original file line number Diff line number Diff line change
@@ -18,26 +18,35 @@ class cpp_type_alias final : public cpp_entity

/// \returns A newly created and registered type alias.
static std::unique_ptr<cpp_type_alias> build(const cpp_entity_index& idx, cpp_entity_id id,
std::string name, std::unique_ptr<cpp_type> type);
std::string name, std::unique_ptr<cpp_type> type,
bool use_c_style = false);

/// \returns A newly created type alias that isn't registered.
/// \notes This function is intendend for templated type aliases.
static std::unique_ptr<cpp_type_alias> build(std::string name, std::unique_ptr<cpp_type> type);
static std::unique_ptr<cpp_type_alias> build(std::string name, std::unique_ptr<cpp_type> type,
bool use_c_style = false);

/// \returns A reference to the aliased [cppast::cpp_type]().
const cpp_type& underlying_type() const noexcept
{
return *type_;
}

/// \returns Whether to generate C-style typedefs instead of C++ using statements
bool use_c_style() const noexcept
{
return use_c_style_;
}

private:
cpp_type_alias(std::string name, std::unique_ptr<cpp_type> type)
: cpp_entity(std::move(name)), type_(std::move(type))
cpp_type_alias(std::string name, std::unique_ptr<cpp_type> type, bool use_c_style)
: cpp_entity(std::move(name)), type_(std::move(type)), use_c_style_(use_c_style)
{}

cpp_entity_kind do_get_entity_kind() const noexcept override;

std::unique_ptr<cpp_type> type_;
bool use_c_style_;
};
} // namespace cppast

2 changes: 1 addition & 1 deletion include/cppast/cppast_fwd.hpp
Original file line number Diff line number Diff line change
@@ -102,7 +102,7 @@ enum class visit_filter;

enum cpp_access_specifier_kind : int;
enum cpp_builtin_type_kind : int;
enum cpp_cv : int;
enum cpp_cv_flags : int;
enum cpp_function_body_kind : int;
enum cpp_reference : int;
enum cpp_storage_class_specifiers : int;
3 changes: 3 additions & 0 deletions include/cppast/libclang_parser.hpp
Original file line number Diff line number Diff line change
@@ -166,10 +166,13 @@ class libclang_compile_config final : public compile_config
return "libclang";
}

bool do_use_c() const noexcept override;

std::string clang_binary_;
bool write_preprocessed_ : 1;
bool fast_preprocessing_ : 1;
bool remove_comments_in_macro_ : 1;
bool use_c_ : 1;

friend detail::libclang_compile_config_access;
};
60 changes: 40 additions & 20 deletions src/code_generator.cpp
Original file line number Diff line number Diff line change
@@ -241,13 +241,25 @@ bool generate_type_alias(code_generator& generator, const cpp_type_alias& alias,
code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), cur_access);
if (output)
{
output << keyword("using") << whitespace << identifier(alias.name()) << operator_ws
<< punctuation("=") << operator_ws;
if (output.options() & code_generator::exclude_target)
output.excluded(alias);
if (alias.use_c_style())
{
output << keyword("typedef") << whitespace;
if (output.options() & code_generator::exclude_target)
output.excluded(alias);
else
detail::write_type(output, alias.underlying_type(), alias.name());
output << punctuation(";") << newl;
}
else
detail::write_type(output, alias.underlying_type(), "");
output << punctuation(";") << newl;
{
output << keyword("using") << whitespace << identifier(alias.name()) << operator_ws
<< punctuation("=") << operator_ws;
if (output.options() & code_generator::exclude_target)
output.excluded(alias);
else
detail::write_type(output, alias.underlying_type(), "");
output << punctuation(";") << newl;
}
}
return static_cast<bool>(output);
}
@@ -471,6 +483,8 @@ void write_storage_class(code_generator::output& output, cpp_storage_class_speci
output << keyword("extern") << whitespace;
if (is_thread_local(storage))
output << keyword("thread_local") << whitespace;
if (is_register(storage))
output << keyword("register") << whitespace;
if (is_constexpr)
output << keyword("constexpr") << whitespace;
else if (is_consteval)
@@ -658,22 +672,28 @@ void write_suffix_virtual(code_generator::output& output, const cpp_virtual& vir
bool write_cv_ref(code_generator::output& output, const cpp_member_function_base& base)
{
auto need_ws = false;
switch (base.cv_qualifier())
auto cv = base.cv_qualifier();

std::vector<const char*> qualifiers;
if (is_const(cv))
qualifiers.push_back("const");
if (is_volatile(cv))
qualifiers.push_back("volatile");
if (is_atomic(cv))
qualifiers.push_back("_Atomic");
if (is_restrict(cv))
qualifiers.push_back("restrict");

bool first = true;
for (auto& q : qualifiers)
{
case cpp_cv_none:
break;
case cpp_cv_const:
output << operator_ws << keyword("const");
need_ws = true;
break;
case cpp_cv_volatile:
output << operator_ws << keyword("volatile");
need_ws = true;
break;
case cpp_cv_const_volatile:
output << operator_ws << keyword("const") << whitespace << keyword("volatile");
if (first)
output << operator_ws;
else
output << whitespace;
output << keyword(std::move(q));
first = false;
need_ws = true;
break;
}

switch (base.ref_qualifier())
4 changes: 4 additions & 0 deletions src/cpp_member_function.cpp
Original file line number Diff line number Diff line change
@@ -15,6 +15,10 @@ std::string cpp_member_function_base::do_get_signature() const
result += " const";
if (is_volatile(cv_qualifier()))
result += " volatile";
if (is_atomic(cv_qualifier()))
result += " _Atomic";
if (is_restrict(cv_qualifier()))
result += " restrict";

if (ref_qualifier() == cpp_ref_lvalue)
result += " &";
Loading