Why is it impossible to use NULL as default pointer argument inside templated function? Lets concider the following code:
template<class Graph, class NodeAttribs, class ArcAttribs> string
graphToGraphviz(Graph &graph,
NodeAttribs *nattribs = NULL,
ArcAttribs *aattribs = NULL,
string name = ""){
/*...*/
}
I want to be able to call it like this:
graphToGraphviz(g);
I have suspections, that compiler thinks it cannot resolve the types for NULL, but these types are not used if the attribute is NULL (there are if conditions). But maybe this case could not be resolved the proper way by compiler. If yes, how could I write such overloaded function, which will allow me to use the short form?
I have an idea of overloading it like that:
class Empty{}
template<class Graph> string
graphToGraphViz(Graph &graph,
string name = ""){
return graphToGraphviz<Graph, Empty, Empty>(graph, NULL, NULL, name)
}
but then the compiler gives me errors, among others, that class Empty has no operator [] defined. This is again understable, but do I have to make all these "dummy" operator overloadings and empty functions to satify compiler or is there a better way to do this?
EDIT: Please take a look at the full source code - it converts Lemon graph to graphviz format: I've tried to use the new syntax from C++11 (as the answers below suggest), but without success.
#ifndef GRAPHTOGRAPHVIZ_H_
#define GRAPHTOGRAPHVIZ_H_
#include <lemon/list_graph.h>
using namespace lemon;
using namespace std;
/* USAGE:
* ListDigraph::NodeMap<unordered_map<string, string>> nodeAttribs(g);
* ListDigraph::ArcMap<unordered_map<string, string>> arcAttribs(g);
* nodeAttribs[node]["label"] = "node_label";
* string dot = graphToGraphviz(g, &nodeAttribs, &arcAttribs, "hello");
*/
template<class Map>
string getAttribs(Map &map){
string attribs = "";
for (const auto &el : map){
if (el.second != "")
attribs += "\"" + el.first + "\"=\"" + el.second + "\",";
}
if (attribs != "")
attribs = " [" + attribs + "]";
return attribs;
}
template<class Graph, class NodeAttribs, class ArcAttribs> string
graphToGraphviz(Graph &graph,
NodeAttribs *nattribs = NULL,
ArcAttribs *aattribs = NULL,
string name = ""){
typedef typename Graph::template NodeMap<string> NodeMap;
typedef typename Graph::NodeIt NodeIterator;
typedef typename Graph::ArcIt ArcIterator;
NodeMap labels(graph);
ostringstream layout;
layout << "strict digraph \""+name+"\" {\n";
// prepare labels
for (NodeIterator node(graph); node != INVALID; ++node){
string label = "";
if (*nattribs != NULL)
label = (*nattribs)[node]["label"];
if (label == "") label = static_cast<ostringstream*>( &(ostringstream() << graph.id(node)) )->str();
label = "\"" + label + "\"";
labels[node] = label;
}
// initialize nodes
for (NodeIterator node(graph); node != INVALID; ++node){
layout << labels[node];
if (*nattribs != NULL)
layout << getAttribs((*nattribs)[node]);
layout << ";" << std::endl;
}
// initialize arcs
for (ArcIterator arc(graph); arc != INVALID; ++arc){
layout << labels[graph.source(arc)] << "->" << labels[graph.target(arc)];
if (*aattribs != NULL)
layout << getAttribs((*aattribs)[arc]);
layout << ";" << std::endl;
}
layout << "}";
return layout.str();
}
#endif /* GRAPHTOGRAPHVIZ_H_ */
with C++11 syntax the function header will look like:
template<class Graph, class NodeAttribs=ListDigraph::NodeMap<string>, class ArcAttribs=ListDigraph::NodeMap<string> > string
graphToGraphviz(Graph &graph,
NodeAttribs *nattribs = NULL,
ArcAttribs *aattribs = NULL,
string name = "")
but it does not compile and gives a tons of strange errors.
Emptyclass is probably the easiest. You could also use partial specialization, but that could be overkilling, depending on the complexity of the code.traitsclass and partially specialize these...