Header c14n.h cannot be included first
The following C++ code will not compile:
#include <libxml/c14n.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
auto main() -> int
{
LIBXML_TEST_VERSION
auto doc = ::xmlParseFile("data.xml");
::xmlC14NDocSave(doc, nullptr, XML_C14N_1_0, nullptr, 1, "out.xml", 0);
::xmlFreeDoc(doc);
::xmlCleanupParser();
}
The errors are that xmlC14NDocSave()
and XML_C14N_1_0
have not been declared.
Simply reordering the includes so that c14n.h
isn’t first fixes this:
#include <libxml/parser.h>
#include <libxml/c14n.h>
#include <libxml/tree.h>
auto main() -> int
{
LIBXML_TEST_VERSION
auto doc = ::xmlParseFile("data.xml");
::xmlC14NDocSave(doc, nullptr, XML_C14N_1_0, nullptr, 1, "out.xml", 0);
::xmlFreeDoc(doc);
::xmlCleanupParser();
}
Digging into the issue, I noticed that the first (functional) lines of c14n.h
are:
#ifndef __XML_C14N_H__
#define __XML_C14N_H__
#ifdef LIBXML_C14N_ENABLED
#ifdef LIBXML_OUTPUT_ENABLED
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include <libxml/xmlversion.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
Notice that LIBXML_C14N_ENABLED
and LIBXML_OUTPUT_ENABLED
are tested before anything else (other than the include guard), including the #include <libxml/xmlversion.h>
where they are defined.
So as an experiment, I included <libxml/xmlversion.h>
first:
#include <libxml/xmlversion.h>
#include <libxml/c14n.h>
auto main() -> int {}
This triggered a stream of errors so long it blew through my terminal buffer. They all seem to be triggered by the fact that c14n.h
opens a extern "C" {
block then includes other libxml headers which transitively include standard C (or C++) headers, which choke on definitions of C++ stuff that can’t be in an extern "C"
block.
The reason things worked when including tree.h
(or parser.h
, which includes tree.h
) first is because the only headers c14n.h
(directly) includes are xmlversion.h
(of course), tree.h
, and xpath.h
. And the only standard header xpath.h
includes (indirectly) that isn’t already included via tree.h
is stddef.h
… which is apparently okay to include within extern "C"
, at least in libstdc++. But, just to be safe, to avoid any issues one should include both tree.h
and xpath.h
before including c14n.h
.
The fix seems to be just reordering the opening lines of c14n.h
to something similar to the order in other libxml headers; including <libxml/xmlversion.h>
first, and moving the extern "C"
block to after including the other libxml headers:
#ifndef __XML_C14N_H__
#define __XML_C14N_H__
#include <libxml/xmlversion.h>
#ifdef LIBXML_C14N_ENABLED
#ifdef LIBXML_OUTPUT_ENABLED
#include <libxml/tree.h>
#include <libxml/xpath.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */