diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8ae853b..03c3012 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,12 +2,13 @@ cmake_minimum_required(VERSION 3.13)

PROJECT(ubus C)

-ADD_DEFINITIONS(-Wall -Werror)
+ADD_COMPILE_OPTIONS(-Wall -Werror)
IF(CMAKE_C_COMPILER_VERSION VERSION_GREATER 6)
-	ADD_DEFINITIONS(-Wextra -Werror=implicit-function-declaration)
-	ADD_DEFINITIONS(-Wformat -Werror=format-security -Werror=format-nonliteral)
+  ADD_COMPILE_OPTIONS(-Wextra -Wformat -Werror=format-security -Werror=format-nonliteral)
+  ADD_COMPILE_OPTIONS($<$<COMPILE_LANGUAGE:C>:-Werror=implicit-function-declaration>)
ENDIF()
-ADD_DEFINITIONS(-Os -std=gnu99 -g3 -Wmissing-declarations -Wno-unused-parameter)
+ADD_COMPILE_OPTIONS(-Os -g3 -Wmissing-declarations -Wno-unused-parameter)
+ADD_COMPILE_OPTIONS($<$<COMPILE_LANGUAGE:C>:-std=gnu99>)

OPTION(BUILD_LUA "build Lua plugin" ON)
OPTION(BUILD_EXAMPLES "build examples" ON)
diff --git a/libubus.h b/libubus.h
index b74a823..fcf62c8 100644
--- a/libubus.h
+++ b/libubus.h
@@ -69,41 +69,44 @@ typedef bool (*ubus_new_object_handler_t)(struct ubus_context *ctx, struct ubus_
	{						\
		.name = _name,				\
		.id = 0,				\
-		.n_methods = ARRAY_SIZE(_methods),	\
-		.methods = _methods			\
+		.methods = _methods,			\
+		.n_methods = ARRAY_SIZE(_methods)	\
	}

-#define __UBUS_METHOD_NOARG(_name, _handler, _tags)	\
-	.name = _name,					\
-	.handler = _handler,				\
+#define __UBUS_METHOD_BASE(_name, _handler, _mask, _tags)	\
+	.name = _name,						\
+	.handler = _handler,					\
+	.mask = _mask,						\
	.tags = _tags

-#define __UBUS_METHOD(_name, _handler, _policy, _tags)	\
-	__UBUS_METHOD_NOARG(_name, _handler, _tags),	\
-	.policy = _policy,				\
+#define __UBUS_METHOD_NOARG(_name, _handler, _mask, _tags)	\
+	__UBUS_METHOD_BASE(_name, _handler, _mask, _tags),	\
+	.policy = NULL,						\
+	.n_policy = 0
+
+#define __UBUS_METHOD(_name, _handler, _mask, _policy, _tags)	\
+	__UBUS_METHOD_BASE(_name, _handler, _mask, _tags),	\
+	.policy = _policy,					\
	.n_policy = ARRAY_SIZE(_policy)

#define UBUS_METHOD(_name, _handler, _policy)		\
-	{ __UBUS_METHOD(_name, _handler, _policy, 0) }
+	{ __UBUS_METHOD(_name, _handler, 0, _policy, 0) }

#define UBUS_METHOD_TAG(_name, _handler, _policy, _tags)\
-	{ __UBUS_METHOD(_name, _handler, _policy, _tags) }
+	{ __UBUS_METHOD(_name, _handler, 0, _policy, _tags) }

#define UBUS_METHOD_MASK(_name, _handler, _policy, _mask) \
-	{						\
-		__UBUS_METHOD(_name, _handler, _policy, 0),\
-		.mask = _mask				\
-	}
+	{ __UBUS_METHOD(_name, _handler, _mask, _policy, 0) }

#define UBUS_METHOD_NOARG(_name, _handler)		\
-	{ __UBUS_METHOD_NOARG(_name, _handler, 0) }
+	{ __UBUS_METHOD_NOARG(_name, _handler, 0, 0) }

#define UBUS_METHOD_TAG_NOARG(_name, _handler, _tags)	\
-	{ __UBUS_METHOD_NOARG(_name, _handler, _tags) }
+	{ __UBUS_METHOD_NOARG(_name, _handler, 0, _tags) }

-#define UBUS_TAG_STATUS		BIT(0)
-#define UBUS_TAG_ADMIN		BIT(1)
-#define UBUS_TAG_PRIVATE	BIT(2)
+#define UBUS_TAG_STATUS		(1ul << 0)
+#define UBUS_TAG_ADMIN		(1ul << 1)
+#define UBUS_TAG_PRIVATE	(1ul << 2)

struct ubus_method {
	const char *name;
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 0cb3342..5549a40 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -6,6 +6,12 @@ MACRO(ADD_UNIT_TEST name)
  TARGET_INCLUDE_DIRECTORIES(${name} PRIVATE ${PROJECT_SOURCE_DIR})
ENDMACRO(ADD_UNIT_TEST)

+MACRO(ADD_UNIT_TEST_CPP name)
+  ADD_EXECUTABLE(${name} ${name}.cpp)
+  TARGET_LINK_LIBRARIES(${name} ubox blobmsg_json json_script ${json})
+  TARGET_INCLUDE_DIRECTORIES(${name} PRIVATE ${PROJECT_SOURCE_DIR})
+ENDMACRO(ADD_UNIT_TEST_CPP)
+
FILE(GLOB test_cases "test-*.c")
FOREACH(test_case ${test_cases})
  GET_FILENAME_COMPONENT(test_case ${test_case} NAME_WE)
@@ -13,6 +19,11 @@ FOREACH(test_case ${test_cases})
  ADD_UNIT_TEST_SAN(${test_case})
ENDFOREACH(test_case)

+ENABLE_LANGUAGE(CXX)
+ADD_COMPILE_OPTIONS($<$<COMPILE_LANGUAGE:CXX>:-std=gnu++11>)
+ADD_UNIT_TEST_CPP(test-cplusplus)
+ADD_TEST(NAME cplusplus COMMAND test-cplusplus)
+
IF(CMAKE_C_COMPILER_ID STREQUAL "Clang")
  ADD_SUBDIRECTORY(fuzz)
ENDIF()
diff --git a/tests/test-cplusplus.cpp b/tests/test-cplusplus.cpp
new file mode 100644
index 0000000..89c5ffb
--- /dev/null
+++ b/tests/test-cplusplus.cpp
@@ -0,0 +1,34 @@
+#include "libubus.h"
+
+// Ensure UBUS_* macros can be used from C++
+
+static int handler(ubus_context *, ubus_object *, ubus_request_data *, const char *, blob_attr *)
+{
+	return 0;
+}
+
+enum {
+	HELLO_ID,
+	HELLO_MSG,
+};
+
+constexpr blobmsg_policy hello_policy[] = {
+	[HELLO_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },
+	[HELLO_MSG] = { .name = "msg", .type = BLOBMSG_TYPE_STRING },
+};
+
+constexpr ubus_method test_methods[] = {
+	UBUS_METHOD("hello1", handler, hello_policy),
+	UBUS_METHOD_TAG("hello2", handler, hello_policy, UBUS_TAG_ADMIN | UBUS_TAG_PRIVATE),
+	UBUS_METHOD_MASK("hello3", handler, hello_policy, 0),
+	UBUS_METHOD_NOARG("hello4", handler),
+	UBUS_METHOD_TAG_NOARG("hello5", handler, UBUS_TAG_STATUS),
+};
+
+constexpr ubus_object_type test_object_type = UBUS_OBJECT_TYPE("test", test_methods);
+
+int main()
+{
+	(void) test_object_type;
+	return 0;
+}
