Saturday, 19 April 2014

Recursive C++ include processor for GLSL and other C-style languages

The important part here is how and where we insert #line directives. C-style preprocessors interpret this as telling them where to look, and in what source file, if there's an error or warning to be reported, e.g.

#line 12 "C:/code/media/shaders/myshader.glsl"

We put one of these at the top of each included file, plus the main file. And we put one after each included file is inlined to tell the preprocessor we're back in the parent. Thus instead of the useless GL shader compiler message:
0(4) : warning C7555: 'varying' is deprecated, use 'in/out' instead

- we get a useful one like this:
../../media/shaders/myshader.glsl(4) : warning C7555: 'varying' is deprecated, use 'in/out' instead

And in Visual Studio, you can double-click on this line in the Output window, it will zoom straight to the offending line!

void ProcessIncludes(std::string &src,std::string &filenameUtf8)
{
	size_t pos=0;
	src=src.insert(0,base::stringFormat("#line 0 \"%s\"\r\n",filenameUtf8.c_str()));

	int next=(int)src.find('\n',pos+1);
	int line_number=0;
	while(next>=0)
	{
		std::string line=src.substr(pos+1,next-pos);
		int inc=line.find("#include");
		if(inc==0)
		{
			int start_of_line=(int)pos+1;
			pos+=9;
		int n=(int)src.find("\n",pos+1);
		int r=(int)src.find("\r",pos+1);
			int eol=n;
			if(r>=0&&r<n)
				eol=r;
			std::string include_file=line.substr(10,line.length()-13);
			src=src.insert(start_of_line,"//");
			// Go to after the newline at the end of the #include statement. Two for "//" and two for "\r\n"
			eol+=4;
			std::string includeFilenameUtf8	=GetFileLoader()->FindFileInPathStack(include_file.c_str(),shaderPathsUtf8);
			std::string newsrc=loadShaderSource(includeFilenameUtf8.c_str());
			ProcessIncludes(newsrc,includeFilenameUtf8);
			//First put the "restore" #line directive after the commented-out #include.
			src=src.insert(eol,base::stringFormat("\r\n#line %d \"%s\"\r\n",line_number,filenameUtf8.c_str()));
			// Now insert the contents of the #include file before the closing #line directive.
			src=src.insert(eol,newsrc);
			next+=newsrc.length();
			line_number--;
		}
		else
			line_number++;
		pos=next;
		next=(int)src.find('\n',pos+1);
	}
}