2

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.

3
  • Your idea of the Empty class is probably the easiest. You could also use partial specialization, but that could be overkilling, depending on the complexity of the code. Commented Nov 23, 2012 at 8:12
  • 2
    @rodrigo: you cannot partially specialize a function. Commented Nov 23, 2012 at 8:24
  • @n.m.: Oh, right! You could, however, use some kind of traits class and partially specialize these... Commented Nov 23, 2012 at 11:44

3 Answers 3

2

Compiler has the problem when you call:

graphToGraphviz(g);

Now what is the type of NodeAttribs and ArcAttribs ?
Compiler has to deduce its type irrespective of you are using it or not. Because using or not using is a runtime check.
With your current code, the above mentioned types become non-deducible.

how could I write such overloaded function

Your question has the answer !!
Overload the template function, remove the default arguments from your original template function and let both the functions co-exist:

template<class Graph>
string graphToGraphviz(Graph &graph, string name = "");
Sign up to request clarification or add additional context in comments.

3 Comments

Of course such overloading would be the easiest method, but imagine code, which is hardly dividable and I want to call the base function from my overloaded one. Then I'm Again facing the problem.
@danilo2 No, you're not. In the overloaded one, you can specify the template arguments explicitly, for example: return graphToViz<Graph, void, void>(graph, NULL, NULL, name);
Yes of course, but then you are facing the same problem I was asking about - during compilation of return graphToViz<Graph, void, void>(graph, NULL, NULL, name); the compiler throws errors that he cannot resolve the template type of NULL.
1

If you are using C++11, you can do this:

template<class Graph, class NodeAttribs=Empty, class ArcAttribs=Empty> ...

I have not found relevant language in the standard, but gcc accepts it.

2 Comments

I've tried this version but it did not worked - please take a look at the source code I attached to my question.
You have errors in the function body. You also need to use default arguments that work, not Empty but real attribute maps. This compiles for me with g++-4.6.1 -std=c++0x.
1

Have you tried

template<class Graph, class NodeAttribs, class ArcAttribs> string
graphToGraphviz(Graph       &graph,
                NodeAttribs *nattribs = (<NodeAttribsClass>*)NULL,
                ArcAttribs  *aattribs = (<ArcAttribsClass>*)NULL,
                string      name      = ""){
   /*...*/
}

OR

template<class Graph, class NodeAttribs = NodeAttribsClass, class ArcAttribs = ArcAttribsClass> string
graphToGraphviz(Graph       &graph,
                NodeAttribs *nattribs = NULL,
                ArcAttribs  *aattribs = NULL,
                string      name      = ""){
   /*...*/
}

Where NodeAttribsClass and ArcAttribsClass are valid (concrete) classes that can be used in that slot?

1 Comment

I've tried this version but it didnt worked for me - please take a look at the source code I added to the first post.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.