tools/net/ynl/lib/Makefile | 5 +++- tools/net/ynl/lib/ynl-priv.h | 11 ++++++- tools/net/ynl/lib/ynl.c | 58 +++++++++++++++++++++--------------- tools/net/ynl/lib/ynl.h | 19 +++++++++--- 4 files changed, 63 insertions(+), 30 deletions(-)
Compiling ynl.c in a C++ code base requires invoking C compiler and
using extern "C" for the headers. To make it easier, we can add
small changes to the ynl.c file to make it palatable to the native
C++ compiler. The changes are:
- avoid using void* pointer arithmetic, use char* instead
- avoid implicit void* type casts, add c-style explicit casts
- avoid implicit int->enum type casts, add c-style explicit casts
- avoid anonymous structs (for type casts)
- namespacify cpp version, this should let us compile both ynl.c
as c and ynl.c as cpp in the same binary (YNL_CPP can be used
to enable/disable namespacing)
Also add test_cpp rule to make sure ynl.c won't break C++ in the future.
Signed-off-by: Stanislav Fomichev <sdf@fomichev.me>
---
tools/net/ynl/lib/Makefile | 5 +++-
tools/net/ynl/lib/ynl-priv.h | 11 ++++++-
tools/net/ynl/lib/ynl.c | 58 +++++++++++++++++++++---------------
tools/net/ynl/lib/ynl.h | 19 +++++++++---
4 files changed, 63 insertions(+), 30 deletions(-)
diff --git a/tools/net/ynl/lib/Makefile b/tools/net/ynl/lib/Makefile
index 4b2b98704ff9..94f8dc4a31d1 100644
--- a/tools/net/ynl/lib/Makefile
+++ b/tools/net/ynl/lib/Makefile
@@ -11,7 +11,7 @@ OBJS=$(patsubst %.c,%.o,${SRCS})
include $(wildcard *.d)
-all: ynl.a
+all: ynl.a test_cpp
ynl.a: $(OBJS)
@echo -e "\tAR $@"
@@ -23,6 +23,9 @@ ynl.a: $(OBJS)
distclean: clean
rm -f *.a
+test_cpp: ynl.c
+ $(COMPILE.cpp) -DYNL_CPP -o ynl.cc.o $<
+
%.o: %.c
$(COMPILE.c) -MMD -c -o $@ $<
diff --git a/tools/net/ynl/lib/ynl-priv.h b/tools/net/ynl/lib/ynl-priv.h
index 824777d7e05e..1dbb14e760e6 100644
--- a/tools/net/ynl/lib/ynl-priv.h
+++ b/tools/net/ynl/lib/ynl-priv.h
@@ -6,6 +6,10 @@
#include <stddef.h>
#include <linux/types.h>
+#if defined(__cplusplus) && defined(YNL_CPP)
+namespace ynl_cpp {
+#endif
+
struct ynl_parse_arg;
/*
@@ -224,7 +228,7 @@ static inline void *ynl_attr_data_end(const struct nlattr *attr)
#define ynl_attr_for_each_payload(start, len, attr) \
for ((attr) = ynl_attr_first(start, len, 0); attr; \
- (attr) = ynl_attr_next(start + len, attr))
+ (attr) = ynl_attr_next((char *)start + len, attr))
static inline struct nlattr *
ynl_attr_if_good(const void *end, struct nlattr *attr)
@@ -467,4 +471,9 @@ ynl_attr_put_sint(struct nlmsghdr *nlh, __u16 type, __s64 data)
else
ynl_attr_put_s64(nlh, type, data);
}
+
+#if defined(__cplusplus) && defined(YNL_CPP)
+} // namespace ynl_cpp
+#endif
+
#endif
diff --git a/tools/net/ynl/lib/ynl.c b/tools/net/ynl/lib/ynl.c
index 2a169c3c0797..9155b4d5b9f9 100644
--- a/tools/net/ynl/lib/ynl.c
+++ b/tools/net/ynl/lib/ynl.c
@@ -11,6 +11,10 @@
#include "ynl.h"
+#if defined(__cplusplus) && defined(YNL_CPP)
+namespace ynl_cpp {
+#endif
+
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(*arr))
#define __yerr_msg(yse, _msg...) \
@@ -23,13 +27,13 @@
} \
})
-#define __yerr_code(yse, _code...) \
- ({ \
- struct ynl_error *_yse = (yse); \
- \
- if (_yse) { \
- _yse->code = _code; \
- } \
+#define __yerr_code(yse, _code...) \
+ ({ \
+ struct ynl_error *_yse = (yse); \
+ \
+ if (_yse) { \
+ _yse->code = (enum ynl_error_code)_code; \
+ } \
})
#define __yerr(yse, _code, _msg...) \
@@ -149,7 +153,7 @@ ynl_err_walk(struct ynl_sock *ys, void *start, void *end, unsigned int off,
return n;
}
- data_len = end - start;
+ data_len = (char *)end - (char *)start;
ynl_attr_for_each_payload(start, data_len, attr) {
astart_off = (char *)attr - (char *)start;
@@ -192,7 +196,7 @@ ynl_err_walk(struct ynl_sock *ys, void *start, void *end, unsigned int off,
off -= sizeof(struct nlattr);
start = ynl_attr_data(attr);
- end = start + ynl_attr_data_len(attr);
+ end = (char *)start + ynl_attr_data_len(attr);
return n + ynl_err_walk(ys, start, end, off, next_pol,
&str[n], str_sz - n, nest_pol);
@@ -325,12 +329,12 @@ ynl_ext_ack_check(struct ynl_sock *ys, const struct nlmsghdr *nlh,
static int
ynl_cb_error(const struct nlmsghdr *nlh, struct ynl_parse_arg *yarg)
{
- const struct nlmsgerr *err = ynl_nlmsg_data(nlh);
+ const struct nlmsgerr *err = (struct nlmsgerr *)ynl_nlmsg_data(nlh);
unsigned int hlen;
int code;
code = err->error >= 0 ? err->error : -err->error;
- yarg->ys->err.code = code;
+ yarg->ys->err.code = (enum ynl_error_code)code;
errno = code;
hlen = sizeof(*err);
@@ -348,7 +352,7 @@ static int ynl_cb_done(const struct nlmsghdr *nlh, struct ynl_parse_arg *yarg)
err = *(int *)NLMSG_DATA(nlh);
if (err < 0) {
- yarg->ys->err.code = -err;
+ yarg->ys->err.code = (enum ynl_error_code)-err;
errno = -err;
ynl_ext_ack_check(yarg->ys, nlh, sizeof(int));
@@ -366,7 +370,7 @@ int ynl_attr_validate(struct ynl_parse_arg *yarg, const struct nlattr *attr)
unsigned int type, len;
unsigned char *data;
- data = ynl_attr_data(attr);
+ data = (unsigned char *)ynl_attr_data(attr);
len = ynl_attr_data_len(attr);
type = ynl_attr_type(attr);
if (type > yarg->rsp_policy->max_attr) {
@@ -463,7 +467,7 @@ int ynl_submsg_failed(struct ynl_parse_arg *yarg, const char *field_name,
static void ynl_err_reset(struct ynl_sock *ys)
{
- ys->err.code = 0;
+ ys->err.code = YNL_ERROR_NONE;
ys->err.attr_offs = 0;
ys->err.msg[0] = 0;
}
@@ -643,8 +647,8 @@ ynl_get_family_info_mcast(struct ynl_sock *ys, const struct nlattr *mcasts)
if (!ys->n_mcast_groups)
return 0;
- ys->mcast_groups = calloc(ys->n_mcast_groups,
- sizeof(*ys->mcast_groups));
+ ys->mcast_groups = (struct ynl_sock_mcast *)calloc(
+ ys->n_mcast_groups, sizeof(*ys->mcast_groups));
if (!ys->mcast_groups)
return YNL_PARSE_CB_ERROR;
@@ -741,7 +745,8 @@ ynl_sock_create(const struct ynl_family *yf, struct ynl_error *yse)
int sock_type;
int one = 1;
- ys = malloc(sizeof(*ys) + 2 * YNL_SOCKET_BUFFER_SIZE);
+ ys = (struct ynl_sock *)malloc(sizeof(*ys) +
+ 2 * YNL_SOCKET_BUFFER_SIZE);
if (!ys)
return NULL;
memset(ys, 0, sizeof(*ys));
@@ -878,7 +883,7 @@ static int ynl_ntf_parse(struct ynl_sock *ys, const struct nlmsghdr *nlh)
} else {
struct genlmsghdr *gehdr;
- gehdr = ynl_nlmsg_data(nlh);
+ gehdr = (struct genlmsghdr *)ynl_nlmsg_data(nlh);
cmd = gehdr->cmd;
}
@@ -888,7 +893,7 @@ static int ynl_ntf_parse(struct ynl_sock *ys, const struct nlmsghdr *nlh)
if (!info->cb)
return YNL_PARSE_CB_ERROR;
- rsp = calloc(1, info->alloc_sz);
+ rsp = (struct ynl_ntf_base_type *)calloc(1, info->alloc_sz);
rsp->free = info->free;
yarg.data = rsp->data;
yarg.rsp_policy = info->policy;
@@ -933,7 +938,8 @@ int ynl_ntf_check(struct ynl_sock *ys)
/* YNL specific helpers used by the auto-generated code */
-struct ynl_dump_list_type *YNL_LIST_END = (void *)(0xb4d123);
+struct ynl_dump_list_type *YNL_LIST_END =
+ (struct ynl_dump_list_type *)(void *)(0xb4d123);
void ynl_error_unknown_notification(struct ynl_sock *ys, __u8 cmd)
{
@@ -962,7 +968,7 @@ ynl_check_alien(struct ynl_sock *ys, const struct nlmsghdr *nlh, __u32 rsp_cmd)
return -1;
}
- gehdr = ynl_nlmsg_data(nlh);
+ gehdr = (struct genlmsghdr *)ynl_nlmsg_data(nlh);
if (gehdr->cmd != rsp_cmd)
return ynl_ntf_parse(ys, nlh);
}
@@ -973,7 +979,7 @@ ynl_check_alien(struct ynl_sock *ys, const struct nlmsghdr *nlh, __u32 rsp_cmd)
static
int ynl_req_trampoline(const struct nlmsghdr *nlh, struct ynl_parse_arg *yarg)
{
- struct ynl_req_state *yrs = (void *)yarg;
+ struct ynl_req_state *yrs = (struct ynl_req_state *)yarg;
int ret;
ret = ynl_check_alien(yrs->yarg.ys, nlh, yrs->rsp_cmd);
@@ -1006,7 +1012,7 @@ int ynl_exec(struct ynl_sock *ys, struct nlmsghdr *req_nlh,
static int
ynl_dump_trampoline(const struct nlmsghdr *nlh, struct ynl_parse_arg *data)
{
- struct ynl_dump_state *ds = (void *)data;
+ struct ynl_dump_state *ds = (struct ynl_dump_state *)data;
struct ynl_dump_list_type *obj;
struct ynl_parse_arg yarg = {};
int ret;
@@ -1015,7 +1021,7 @@ ynl_dump_trampoline(const struct nlmsghdr *nlh, struct ynl_parse_arg *data)
if (ret)
return ret < 0 ? YNL_PARSE_CB_ERROR : YNL_PARSE_CB_OK;
- obj = calloc(1, ds->alloc_sz);
+ obj = (struct ynl_dump_list_type *)calloc(1, ds->alloc_sz);
if (!obj)
return YNL_PARSE_CB_ERROR;
@@ -1066,3 +1072,7 @@ int ynl_exec_dump(struct ynl_sock *ys, struct nlmsghdr *req_nlh,
yds->first = ynl_dump_end(yds);
return -1;
}
+
+#if defined(__cplusplus) && defined(YNL_CPP)
+} // namespace ynl_cpp
+#endif
diff --git a/tools/net/ynl/lib/ynl.h b/tools/net/ynl/lib/ynl.h
index db7c0591a63f..47a8652f056f 100644
--- a/tools/net/ynl/lib/ynl.h
+++ b/tools/net/ynl/lib/ynl.h
@@ -9,6 +9,10 @@
#include "ynl-priv.h"
+#if defined(__cplusplus) && defined(YNL_CPP)
+namespace ynl_cpp {
+#endif
+
enum ynl_error_code {
YNL_ERROR_NONE = 0,
__YNL_ERRNO_END = 4096,
@@ -56,6 +60,11 @@ struct ynl_family {
unsigned int ntf_info_size;
};
+struct ynl_sock_mcast {
+ unsigned int id;
+ char name[GENL_NAMSIZ];
+};
+
/**
* struct ynl_sock - YNL wrapped netlink socket
* @err: YNL error descriptor, cleared on every request.
@@ -71,10 +80,7 @@ struct ynl_sock {
__u16 family_id;
unsigned int n_mcast_groups;
- struct {
- unsigned int id;
- char name[GENL_NAMSIZ];
- } *mcast_groups;
+ struct ynl_sock_mcast *mcast_groups;
struct ynl_ntf_base_type *ntf_first;
struct ynl_ntf_base_type **ntf_last_next;
@@ -140,4 +146,9 @@ static inline bool ynl_has_ntf(struct ynl_sock *ys)
struct ynl_ntf_base_type *ynl_ntf_dequeue(struct ynl_sock *ys);
void ynl_ntf_free(struct ynl_ntf_base_type *ntf);
+
+#if defined(__cplusplus) && defined(YNL_CPP)
+} // namespace ynl_cpp
+#endif
+
#endif
--
2.50.1
On Thu, 14 Aug 2025 09:44:13 -0700 Stanislav Fomichev wrote: > Compiling ynl.c in a C++ code base requires invoking C compiler and > using extern "C" for the headers. To make it easier, we can add > small changes to the ynl.c file to make it palatable to the native > C++ compiler. The changes are: > - avoid using void* pointer arithmetic, use char* instead > - avoid implicit void* type casts, add c-style explicit casts > - avoid implicit int->enum type casts, add c-style explicit casts > - avoid anonymous structs (for type casts) > - namespacify cpp version, this should let us compile both ynl.c > as c and ynl.c as cpp in the same binary (YNL_CPP can be used > to enable/disable namespacing) > > Also add test_cpp rule to make sure ynl.c won't break C++ in the future. As I mentioned in person, ynl-cpp is a separate thing, and you'd all benefit from making it more C++ than going the other way and massaging YNL C. With that said, commenting below on the few that I think would be okay. > @@ -224,7 +228,7 @@ static inline void *ynl_attr_data_end(const struct nlattr *attr) > > #define ynl_attr_for_each_payload(start, len, attr) \ > for ((attr) = ynl_attr_first(start, len, 0); attr; \ > - (attr) = ynl_attr_next(start + len, attr)) > + (attr) = ynl_attr_next((char *)start + len, attr)) okay > @@ -149,7 +153,7 @@ ynl_err_walk(struct ynl_sock *ys, void *start, void *end, unsigned int off, > return n; > } > > - data_len = end - start; > + data_len = (char *)end - (char *)start; can we make the arguments char * instead of the casts? > static void ynl_err_reset(struct ynl_sock *ys) > { > - ys->err.code = 0; > + ys->err.code = YNL_ERROR_NONE; sure > @@ -56,6 +60,11 @@ struct ynl_family { > unsigned int ntf_info_size; > }; > > +struct ynl_sock_mcast { struct ynl_mcast_grp > + unsigned int id; > + char name[GENL_NAMSIZ]; > +};
On 08/14, Jakub Kicinski wrote: > On Thu, 14 Aug 2025 09:44:13 -0700 Stanislav Fomichev wrote: > > Compiling ynl.c in a C++ code base requires invoking C compiler and > > using extern "C" for the headers. To make it easier, we can add > > small changes to the ynl.c file to make it palatable to the native > > C++ compiler. The changes are: > > - avoid using void* pointer arithmetic, use char* instead > > - avoid implicit void* type casts, add c-style explicit casts > > - avoid implicit int->enum type casts, add c-style explicit casts > > - avoid anonymous structs (for type casts) > > - namespacify cpp version, this should let us compile both ynl.c > > as c and ynl.c as cpp in the same binary (YNL_CPP can be used > > to enable/disable namespacing) > > > > Also add test_cpp rule to make sure ynl.c won't break C++ in the future. > > As I mentioned in person, ynl-cpp is a separate thing, and you'd all > benefit from making it more C++ than going the other way and massaging > YNL C. > > With that said, commenting below on the few that I think would be okay. Ok, and the rest (typecasts mostly and the namespace) - you're not supper happy about? I can drop that test_cpp if that helps :-) > > @@ -224,7 +228,7 @@ static inline void *ynl_attr_data_end(const struct nlattr *attr) > > > > #define ynl_attr_for_each_payload(start, len, attr) \ > > for ((attr) = ynl_attr_first(start, len, 0); attr; \ > > - (attr) = ynl_attr_next(start + len, attr)) > > + (attr) = ynl_attr_next((char *)start + len, attr)) > > okay > > > @@ -149,7 +153,7 @@ ynl_err_walk(struct ynl_sock *ys, void *start, void *end, unsigned int off, > > return n; > > } > > > > - data_len = end - start; > > + data_len = (char *)end - (char *)start; > > can we make the arguments char * instead of the casts? Let me try. That might require to char-ify helpers like ynl_nlmsg_data_offset and ynl_nlmsg_end_addr.
On Thu, 14 Aug 2025 16:36:25 -0700 Stanislav Fomichev wrote: > > As I mentioned in person, ynl-cpp is a separate thing, and you'd all > > benefit from making it more C++ than going the other way and massaging > > YNL C. > > > > With that said, commenting below on the few that I think would be okay. > > Ok, and the rest (typecasts mostly and the namespace) - you're not supper > happy about? I can drop that test_cpp if that helps :-) No, they are illogical from C's standpoint :( I really don't get the need for the adjustments, the code generator is obviously different for C++, and that's a major effort. The ynl.c casts are trivial in comparison. > > > @@ -149,7 +153,7 @@ ynl_err_walk(struct ynl_sock *ys, void *start, void *end, unsigned int off, > > > return n; > > > } > > > > > > - data_len = end - start; > > > + data_len = (char *)end - (char *)start; > > > > can we make the arguments char * instead of the casts? > > Let me try. That might require to char-ify helpers like ynl_nlmsg_data_offset > and ynl_nlmsg_end_addr. Hm, then probably let's leave it as void *
On 8/14/2025 9:44 AM, Stanislav Fomichev wrote: > Compiling ynl.c in a C++ code base requires invoking C compiler and > using extern "C" for the headers. To make it easier, we can add > small changes to the ynl.c file to make it palatable to the native > C++ compiler. The changes are: > - avoid using void* pointer arithmetic, use char* instead > - avoid implicit void* type casts, add c-style explicit casts > - avoid implicit int->enum type casts, add c-style explicit casts > - avoid anonymous structs (for type casts) > - namespacify cpp version, this should let us compile both ynl.c > as c and ynl.c as cpp in the same binary (YNL_CPP can be used > to enable/disable namespacing) > The changes seem ok to me. Obviously these violate several of the usual kernel style guidelines.. But this is a library which has a lot more reason to be compiled with C++. > Also add test_cpp rule to make sure ynl.c won't break C++ in the future. > Thanks. Is this run in some part of automation so we can catch issues from future patches? Ah. I see it is run as part of the normal "all" target. Ok. Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
© 2016 - 2025 Red Hat, Inc.