-
-
Notifications
You must be signed in to change notification settings - Fork 244
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
Would it possible to have more educational things supported? #557
Comments
Hello @GKxxQAQ,
I'm glad that C++ Insights helps you with teaching C++. It's why I created the tool and where I use it the most. Regarding your question, I'm generally open to adding more educational transformations. However, they are the ones with the most effort in development and maintenance. Plus, the risk of showing something incorrect is higher. That means there should be a great benefit such that the long-term effort is worth it.
C++ Insights leaves the preprocessor alone. It only receives the output from the preprocessor run. This can be changed, but I need (very) a convincing example of how it helps.
I agree. Clang doesn't give this information away, so I must hand-code everything. I'm open for a patch.
I let you in on a secret (and obviously everybody who reads this public message :-). I have a Cfront mode in a development branch for about a year. I use it in my training classes. The transformations there are huge, and I keep finding bugs. Hence, I haven't released this transformation yet. However, I plan to publish it once I find enough time again, hopefully around this year. Andreas |
Hi. Thank you for replying to me! As for the compiler-generated special member functions, recently I'm also learning clang libtooling and I am trying to implement a tool (not yet finished) that can hand-code the implicitly-defined functions. Inherited constructors are also supported. For example, for the following code #include <vector>
class Node {};
class BinaryOp {
Node left, right;
public:
BinaryOp(const Node &l, const Node &r = {}) : left{l}, right{r} {}
BinaryOp() = default;
};
struct PlusOpImpl {};
struct OpCommon {};
class PlusOp : public OpCommon, public BinaryOp, private PlusOpImpl {
std::vector<int> m_operands;
std::size_t m_size{};
using BinaryOp::BinaryOp;
};
int main() {
PlusOp po({}, {});
BinaryOp bo;
BinaryOp bo2({}, {});
PlusOp po2;
PlusOp po3(po2);
} The output is: // For demonstration purposes only.
// Indicates the "more-than-perfect" forwarding of arguments that happens
// in inherited constructors.
// std::forward is not perfect enough, because it is not used together
// with universal references.
template <typename T>
auto perfectly_forward(T &&other);
#include <vector>
class Node {
public:
// trivial.
inline constexpr Node() noexcept {}
// trivial.
inline constexpr Node(const Node &) noexcept {}
// trivial.
inline ~Node() noexcept {}
};
class BinaryOp {
Node left;
Node right;
public:
BinaryOp(const Node &l, const Node &r = {}) : left{l}, right{r} {}
// trivial, explicitly defaulted.
inline constexpr BinaryOp() noexcept
// The subobjects are initialized as follows:
// left is default-initialized (calling Node::Node()).
// right is default-initialized (calling Node::Node()).
{}
// trivial.
inline constexpr BinaryOp(const BinaryOp &other) noexcept
: left(other.left),
right(other.right) {}
// trivial.
inline ~BinaryOp() noexcept {}
// The subobjects are destroyed as follows:
// right.~Node();
// left.~Node();
};
struct PlusOpImpl {
// trivial.
inline constexpr PlusOpImpl() noexcept {}
// trivial.
inline constexpr PlusOpImpl(const PlusOpImpl &) noexcept {}
// trivial.
inline ~PlusOpImpl() noexcept {}
};
struct OpCommon {
// trivial.
inline constexpr OpCommon() noexcept {}
// trivial.
inline constexpr OpCommon(const OpCommon &) noexcept {}
// trivial.
inline ~OpCommon() noexcept {}
};
class PlusOp : public OpCommon, public BinaryOp, private PlusOpImpl {
std::vector<int> m_operands;
std::size_t m_size {};
public:
inline PlusOp() noexcept
// The subobjects are initialized as follows:
// base class OpCommon is default-initialized (calling OpCommon::OpCommon()).
// base class BinaryOp is default-initialized (calling BinaryOp::BinaryOp()).
// base class PlusOpImpl is default-initialized (calling PlusOpImpl::PlusOpImpl()).
// m_operands is default-initialized (calling vector<int>::vector<int>()).
// m_size is list-initialized from {} (in-class init).
{}
inline PlusOp(const PlusOp &other) noexcept(false)
: OpCommon(other),
BinaryOp(other),
PlusOpImpl(other),
m_operands(other.m_operands),
m_size(other.m_size) {}
// inherited by using BinaryOp::BinaryOp;
inline PlusOp(Node const &__arg0, Node const &__arg1 = {}) noexcept(false)
: // base class OpCommon is default-initialized (calling OpCommon::OpCommon()).
BinaryOp(perfectly_forward(__arg0),
perfectly_forward(__arg1))
// base class PlusOpImpl is default-initialized (calling PlusOpImpl::PlusOpImpl()).
// m_operands is default-initialized (calling vector<int>::vector<int>()).
// m_size is list-initialized from {} (in-class init).
{}
inline ~PlusOp() noexcept {}
// The subobjects are destroyed as follows:
// m_size (of type std::size_t) is destroyed, which has no effect.
// m_operands.~vector<int>();
// PlusOpImpl::~PlusOpImpl();
// BinaryOp::~BinaryOp();
// OpCommon::~OpCommon();
};
int main() {
PlusOp po({}, {});
BinaryOp bo;
BinaryOp bo2({}, {});
PlusOp po2;
PlusOp po3(po2);
} I'm so surprised to hear that you have implemented a Cfront mode! That's super cool. I'm looking forward to the release of it. |
@andreasfertig While implementing the tool mentioned above I found that Clang sometimes doesn't declare some of the special member functions, which the standard require to be implicitly-declared. Even those that are odr-used (which should be implicitly-defined) can be left out, since to be standard-conforming it only needs to act as if those functions are declared/defined. For example struct X {
int a{0};
};
int main() {
X x;
return x.a;
} The destructor of |
Hello @GKxxQAQ, nice work! I wasn't aware of Andreas |
@andreasfertig I'm so happy to find that you have released the Cfront mode transformations, and the transformations are a lot more than I expect! A slight mistake: Maybe the comment in |
Hello @GKxxQAQ,
My pleasure!
Thanks for spotting this! I will fix it with one of the upcoming fixes. Andreas |
This is a very useful project that helps reveal the things that happen under some fancy syntaxes of C++. As a teaching assistant of the C++ programming course in my university I would very much like to use it to assist with teaching.
The educational coroutines transformation is perfect for learning coroutines. I wanted some other educational tools and was wondering whether they can be integrated into C++ insights. Here are some ideas I could think of:
Better preprocessor output: may be used to explain how include guards work and how circular includes happen. I want something better than what is obtained from the
-E
compiler flag, like folding or indentations to help see the include structure better.Hand-coded compiler-generated special member functions. For example:
As for the copy constructor of
A
, current C++ insights only shows// inline constexpr A(const A &) noexcept(false) = default;
but it might be more helpful to show something like this
Transformation of member functions into non-member functions, just like what Cfront did. This may help students understand how member functions are called, the
this
pointer, the cv-qualification on member functions, etc. For example: Transforminto
Looking forward to your reply.
The text was updated successfully, but these errors were encountered: