c++ return from template - c++

I'm a bit new to generic programming with templates in C++ and have a question on how to return an object from a templated function. This is part of the neural network module of mlpack library. This is from the feedforward_network_test.cpp which can be found here. If I understand this correctly, the way the templated function BuildVanillaNetwork is setup, its possible to pass different types of network parameters to build the neural network. What I would like is for this function to return the FFN object that it builds, so that I can access it from where I call it. I made some small changes to the code over there:
template <typename PerformanceFunction,
typename OutputLayerType,
typename PerformanceFunctionType,
typename MatType = arma::mat
>
mlpack::ann::FFN<> BuildVanillaNetwork(MatType& trainData,
MatType& trainLabels,
MatType& testData,
MatType& testLabels,
const size_t hiddenLayerSize,
const size_t maxEpochs,
const double classificationErrorThreshold)
{
// input layer
mlpack::ann::LinearLayer<> inputLayer(trainData.n_rows, hiddenLayerSize);
mlpack::ann::BiasLayer<> inputBiasLayer(hiddenLayerSize);
mlpack::ann::BaseLayer<PerformanceFunction> inputBaseLayer;
// hidden layer
mlpack::ann::LinearLayer<> hiddenLayer1(hiddenLayerSize, trainLabels.n_rows);
mlpack::ann::BiasLayer<> hiddenBiasLayer1(trainLabels.n_rows);
mlpack::ann::BaseLayer<PerformanceFunction> outputLayer;
// output layer
OutputLayerType classOutputLayer;
auto modules = std::tie(inputLayer, inputBiasLayer, inputBaseLayer, hiddenLayer1, hiddenBiasLayer1, outputLayer);
mlpack::ann::FFN<decltype(modules), decltype(classOutputLayer), mlpack::ann::RandomInitialization, PerformanceFunctionType> net(modules, classOutputLayer);
net.Train(trainData, trainLabels);
MatType prediction;
net.Predict(testData, prediction);
double classificationError;
for (size_t i = 0; i < testData.n_cols; i++)
{
if (arma::sum(arma::sum(arma::abs(prediction.col(i) - testLabels.col(i)))) != 0)
{
classificationError++;
}
}
classificationError = double(classificationError) / testData.n_cols;
std::cout << "Classification Error = " << classificationError * 100 << "%" << std::endl;
return net;
}
And here is the main function:
int main(int argc, char** argv)
{
arma::mat dataset;
mlpack::data::Load("../data/thyroid_train.csv", dataset, true);
arma::mat trainData = dataset.submat(0, 0, dataset.n_rows - 4, dataset.n_cols - 1);
arma::mat trainLabels = dataset.submat(dataset.n_rows - 3, 0, dataset.n_rows - 1, dataset.n_cols - 1);
mlpack::data::Load("../data/thyroid_test.csv", dataset, true);
arma::mat testData = dataset.submat(0, 0, dataset.n_rows - 4, dataset.n_cols - 1);
arma::mat testLabels = dataset.submat(dataset.n_rows - 3, 0, dataset.n_rows - 1, dataset.n_cols - 1);
const size_t hiddenLayerSize = 8;
const size_t maxEpochs = 200;
const double classificationErrorThreshold = 0.1;
auto myFFN = BuildVanillaNetwork<mlpack::ann::LogisticFunction, mlpack::ann::BinaryClassificationLayer, mlpack::ann::MeanSquaredErrorFunction>
(trainData, trainLabels, testData, testLabels, hiddenLayerSize, maxEpochs, classificationErrorThreshold);
return 0;
}
When I compile this, I get the following error:
[100%] Building CXX object CMakeFiles/ff_nn.dir/src/ff_nn.cpp.o /home/username/project-yanack/mlpack_nn/src/ff_nn.cpp:24:18: error: wrong number of template arguments (0, should be 4) mlpack::ann::FFN<> BuildVanillaNetwork(MatType& trainData,
^ In file included from /home/username/project-yanack/mlpack_nn/src/ff_nn.cpp:16:0: /usr/local/include/mlpack/methods/ann/ffn.hpp:35:7: error: provided for ‘template<class LayerTypes, class OutputLayerType, class InitializationRuleType, class PerformanceFunction> class mlpack::ann::FFN’ class FFN
^ /home/username/project-yanack/mlpack_nn/src/ff_nn.cpp: In instantiation of ‘int BuildVanillaNetwork(MatType&, MatType&, MatType&, MatType&, size_t, size_t, double) [with PerformanceFunction
= mlpack::ann::LogisticFunction; OutputLayerType = mlpack::ann::BinaryClassificationLayer; PerformanceFunctionType = mlpack::ann::MeanSquaredErrorFunction; MatType = arma::Mat<double>; size_t = long unsigned int]’: /home/username/project-yanack/mlpack_nn/src/ff_nn.cpp:83:112: required from here /home/username/project-yanack/mlpack_nn/src/ff_nn.cpp:64:12: error: cannot convert ‘mlpack::ann::FFN<std::tuple<mlpack::ann::LinearLayer<arma::Mat<double>, arma::Mat<double> >&, mlpack::ann::BiasLayer<arma::Mat<double>, arma::Mat<double> >&, mlpack::ann::BaseLayer<mlpack::ann::LogisticFunction, arma::Mat<double>, arma::Mat<double> >&, mlpack::ann::LinearLayer<arma::Mat<double>, arma::Mat<double> >&, mlpack::ann::BiasLayer<arma::Mat<double>, arma::Mat<double> >&, mlpack::ann::BaseLayer<mlpack::ann::LogisticFunction, arma::Mat<double>, arma::Mat<double> >&>, mlpack::ann::BinaryClassificationLayer, mlpack::ann::RandomInitialization, mlpack::ann::MeanSquaredErrorFunction>’ to ‘int’ in return
return net;
^ make[2]: *** [CMakeFiles/ff_nn.dir/src/ff_nn.cpp.o] Error 1 make[1]: *** [CMakeFiles/ff_nn.dir/all] Error 2 make: *** [all] Error 2
Any help in fixing this is appreciated. Also, it'll be great if I could get links to a tutorial that explains the various concepts used in this code.
EDIT-1
I changed the function header to this:
template <typename PerformanceFunction,
typename OutputLayerType,
typename PerformanceFunctionType,
typename MatType = arma::mat
>
mlpack::ann::FFN<PerformanceFunction, OutputLayerType, PerformanceFunctionType, MatType> BuildVanillaNetwork(MatType& trainData,
MatType& trainLabels,
MatType& testData,
MatType& testLabels,
const size_t hiddenLayerSize,
const size_t maxEpochs,
const double classificationErrorThreshold)
But I'm still getting error when compiling:
[100%] Building CXX object CMakeFiles/ff_nn.dir/src/ff_nn.cpp.o
In file included from /home/username/project-yanack/mlpack_nn/src/ff_nn.cpp:16:0:
/usr/local/include/mlpack/methods/ann/ffn.hpp: In instantiation of ‘class mlpack::ann::FFN<mlpack::ann::LogisticFunction, mlpack::ann::BinaryClassificationLayer, mlpack::ann::MeanSquaredErrorFunction, arma::Mat<double> >’:
/home/username/project-yanack/mlpack_nn/src/ff_nn.cpp:83:112: required from here
/usr/local/include/mlpack/methods/ann/ffn.hpp:361:55: error: incomplete type ‘std::tuple_size<mlpack::ann::LogisticFunction>’ used in nested name specifier
size_t Max = std::tuple_size<LayerTypes>::value - 1,
^
/usr/local/include/mlpack/methods/ann/ffn.hpp:369:55: error: incomplete type ‘std::tuple_size<mlpack::ann::LogisticFunction>’ used in nested name specifier
size_t Max = std::tuple_size<LayerTypes>::value - 1,
^
/home/username/project-yanack/mlpack_nn/src/ff_nn.cpp: In instantiation of ‘mlpack::ann::FFN<PerformanceFunction, OutputLayerType, PerformanceFunctionType, MatType> BuildVanillaNetwork(MatType&, MatType&, MatType&, MatType&, size_t, size_t, double) [with PerformanceFunction = mlpack::ann::LogisticFunction; OutputLayerType = mlpack::ann::BinaryClassificationLayer; PerformanceFunctionType = mlpack::ann::MeanSquaredErrorFunction; MatType = arma::Mat<double>; size_t = long unsigned int]’:
/home/username/project-yanack/mlpack_nn/src/ff_nn.cpp:83:112: required from here
/home/username/project-yanack/mlpack_nn/src/ff_nn.cpp:64:12: error: could not convert ‘net’ from ‘mlpack::ann::FFN<std::tuple<mlpack::ann::LinearLayer<arma::Mat<double>, arma::Mat<double> >&, mlpack::ann::BiasLayer<arma::Mat<double>, arma::Mat<double> >&, mlpack::ann::BaseLayer<mlpack::ann::LogisticFunction, arma::Mat<double>, arma::Mat<double> >&, mlpack::ann::LinearLayer<arma::Mat<double>, arma::Mat<double> >&, mlpack::ann::BiasLayer<arma::Mat<double>, arma::Mat<double> >&, mlpack::ann::BaseLayer<mlpack::ann::LogisticFunction, arma::Mat<double>, arma::Mat<double> >&>, mlpack::ann::BinaryClassificationLayer, mlpack::ann::RandomInitialization, mlpack::ann::MeanSquaredErrorFunction>’ to ‘mlpack::ann::FFN<mlpack::ann::LogisticFunction, mlpack::ann::BinaryClassificationLayer, mlpack::ann::MeanSquaredErrorFunction, arma::Mat<double> >’
return net;
^
make[2]: *** [CMakeFiles/ff_nn.dir/src/ff_nn.cpp.o] Error 1
make[1]: *** [CMakeFiles/ff_nn.dir/all] Error 2
make: *** [all] Error 2
Also, the signature of the FFN class (here) seems to be different than what I have in this function. Could that be an issue? If it is, how do I fix it since those typenames are not really "types" as far as I understand.
Thanks.

The problem is your definition of the BuildVanillaNetwork function as:
mlpack::ann::FFN<> BuildVanillaNetwork(...)
The error messages are usually hard to read by a human when they involve templates, but reading through the lines gives you something like this:
error: wrong number of template arguments (0, should be 4) ... provided for ‘template class mlpack::ann::FFN’
The rest of the errors are caused by this one (basically, because it doesn't understand the return type of that function, the compiler assumes it to be int, then it complains it can't convert net to int).
So you have to actually specify the template arguments for the return type. You're using decltype in the body of the function to deduce them (which happens at compile time, not runtime), but in the prototype it won't be that easy. There is a way to use decltype to declare the return type of a function, but it won't help you much in this case. So you might as well go on and write them explicitly.

mlpack::ann::FFN<> BuildVanillaNetwork(MatType& trainData,
Change it to something that includes the parameters from the template, somthing like:
mlpack::ann::FFN<
PerformanceFunction,
OutputLayerType,
PerformanceFunctionType,
MatType
> BuildVanillaNetwork(MatType& trainData,...
Or try decltype(auto) if your compiler supports it:
auto BuildVanillaNetwork(MatType& trainData,`...) -> decltype(auto) {...

You can use the following pattern to somewhat simplify the return type inference:
template<typename PerformanceFunction, typename OutputLayerType,
typename PerformanceFunctionType, typename MatType>
struct BuildVanillaNetworkHelper
{
using LinearLayer = mlpack::ann::LinearLayer<>;
using BiasLayer = mlpack::ann::BiasLayer<>;
using BaseLayer = mlpack::ann::BaseLayer<PerformanceFunction>;
using ModulesType = std::tuple<LinearLayer, BiasLayer, BaseLayer,
LinearLayer, BiasLayer, BaseLayer>;
using FFNType = mlpack::ann::FFN<ModulesType, OutputLayerType,
mlpack::ann::RandomInitialization, PerformanceFunctionType>;
};
template <typename PerformanceFunction,
typename OutputLayerType,
typename PerformanceFunctionType,
typename MatType = arma::mat,
typename Helper = BuildVanillaNetworkHelper<
PerformanceFunction, OutputLayerType,
PerformanceFunctionType, MatType>
>
typename Helper::FFNType BuildVanillaNetwork(...);

Related

I'm getting an error when trying to compile this constructor

I have a class with a constructor but the compilation fails with the error:
error: use of deleted function 'std::atomic<long unsigned int>::atomic(const std::atomic<long unsigned int>&)'
I'm struggling to understand what this error means. I've been trying to update some code from using tbb::atomic to using std::atomic, and this error wasn't appearing before with tbb::atomic.
The bit of code that upsets the compiler is:
#include <boost/aligned_storage.hpp>
template <typename T>
class Holder<T,true>
{
public:
//! Construct a value (there mustn't be one already)
template <typename ARG>
void Construct(const ARG &arg) { new(Raw()) T(arg); }
private:
typename boost::aligned_storage<sizeof(T), boost::alignment_of<T>::value>::type impl;
void *Raw() { return &impl; }
};
Specifically the line:
void Construct(const ARG &arg) { new(Raw()) T(arg); }
I'd be very grateful if anyone could help me understand what is going on.
EDIT:
Full error message:
holder.hpp:75:34: error: use of deleted function 'std::atomic<long unsigned int>::atomic(const std::atomic<long unsigned int>&)'
75 | void Construct(const T &arg) { new(Raw()) T(arg); }
| ^~~~~~~~~~~~~~~~~
In file included from /include/TPtools/boost/smart_ptr/detail/sp_counted_base_std_atomic.hpp:19,
from /include/TPtools/boost/smart_ptr/detail/sp_counted_base.hpp:48,
from /include/TPtools/boost/smart_ptr/detail/shared_count.hpp:29,
from /include/TPtools/boost/smart_ptr/shared_ptr.hpp:28,
from /include/TPtools/boost/shared_ptr.hpp:17,
from movegate.cpp:14:
/pkgsData/gcc-v9.3.0p1/Linux/RHEL6.0-2013-x86_64/include/c++/9.3.0/atomic:824:7: note: declared here
824 | atomic(const atomic&) = delete;
| ^~~~~~
holder.hpp: In instantiation of 'void Holder<T, true>::Construct(const T&) [with T = std::atomic<long unsigned int>; T = std::atomic<long unsigned int>]':
element.hpp:185:9: required from 'void Element<PRESENT, ABSENT>::Accessor<PRESENCE>::MakePresentImpl(const ARG&) const [with ARG = std::atomic<long unsigned int>; PRESENCE = Cluster<Element<std::atomic<long unsigned int>, Link>, true>::NonLockingPresence; PRESENT = std::atomic<long unsigned int>; ABSENT = Link]'
element.hpp:119:7: required from 'Element<PRESENT, ABSENT>::PresentResult Element<PRESENT, ABSENT>::Accessor<PRESENCE>::MakePresent(const ARG&) const [with ARG = std::atomic<long unsigned int>; PRESENCE = Cluster<Element<std::atomic<long unsigned int>, Link>, true>::NonLockingPresence; PRESENT = std::atomic<long unsigned int>; ABSENT = Link; Element<PRESENT, ABSENT>::PresentResult = std::atomic<long unsigned int>&]'
dispenser.hpp:78:5: required from 'Index Dispenser<T>::Insert(const T&) [with T = std::atomic<long unsigned int>]'

C++: confusing variadic template causing "candidate function not viable" error

I was trying to port a CUDA project into ROCm platform which make use of C++ templates extensively. During this process, I am getting following compilation error
/root/warp-ctc/include/detail/gpu_ctc.h:381:5: error: no matching function for call to 'hipLaunchKernelGGL'
hipLaunchKernelGGL((prepare_stable_SM_kernel<ProbT, VT>), dim3(grid_size), dim3(NT), 0, stream_, ctc_helper::identity<ProbT>(), probs_,
^~~~~~~~~~~~~~~~~~
.....
.....
/opt/rocm/hip/include/hip/hcc_detail/functional_grid_launch.hpp:138:13: note: candidate function [with Args = <ctc_helper::identity<float, float>, float *,
float *, int, int>, F = void (*)(ctc_helper::identity<float, float>, float *, float *, int, int)] not viable: no overload of 'prepare_stable_SM_kernel'
matching 'void (*)(ctc_helper::identity<float, float>, float *, float *, int, int)' for 1st argument
inline void hipLaunchKernelGGL(F kernel, const dim3& numBlocks, const dim3& dimBlocks,
for following piece of C++ code
hipLaunchKernelGGL((prepare_stable_SM_kernel<ProbT, VT>),
dim3(grid_size),
dim3(NT),
0,
stream_,
ctc_helper::identity<ProbT>(),
probs_,
denoms_,
out_dim_,
num_elements);
where hipLaunchKernelGGL is defined as
template <typename... Args, typename F = void (*)(Args...)>
inline void hipLaunchKernelGGL(F kernel,
const dim3& numBlocks,
const dim3& dimBlocks,
std::uint32_t sharedMemBytes,
hipStream_t stream,
Args... args) {
// ...
// ...
}
and prepare_stable_SM_kernel is defined as
template <typename ProbT, int VT = 1, typename Op>
__global__ void prepare_stable_SM_kernel(Op f, ProbT* probs,
const ProbT* const col_max,
int alphabet_size,
int count) {
// ...
}
Anyone please help me by providing some hints to fix this problem.
This error was fixed by adding a third parameter ctc_helper::identity<ProbT> to prepare_stable_SM_kernel function
Ref https://github.com/harish2704/warp-ctc/commit/c7ec45febc9c0077ffa35932b3d11a05daf8bf7c#diff-1e372cfc8edab301b6c284e9db8dbf68

Tensorflow C++ Placeholder initialization

I was trying to perform image captioning with tensorflow on cpp. I was following the direction on How to import an saved Tensorflow model train using tf.estimator and predict on input data and tried
int main(int argc, char**) {
....
auto x = tf::Tensor(tf::DT_UINT8, tf::TensorShape({height, width, channel}));
auto matrix = x.matrix<char>();
....
}
When I compile the program, the code
> In file included from /usr/local/include/tensorflow/core/framework/tensor.h:26:0,
from /usr/local/include/tensorflow/core/public/session.h:23,
from /usr/local/include/tensorflow/cc/saved_model/loader.h:26,
from server.cpp:22:
/usr/local/include/tensorflow/core/framework/types.h: In instantiation of ‘struct tensorflow::DataTypeToEnum<char>’:
/usr/local/include/tensorflow/core/framework/tensor.h:530:46: required from ‘typename tensorflow::TTypes<T, NDIMS>::Tensor tensorflow::Tensor::tensor() [with T = char; long unsigned int NDIMS = 2ul; typename tensorflow::TTypes<T, NDIMS>::Tensor = Eigen::TensorMap<Eigen::Tensor<char, 2, 1, long int>, 16, Eigen::MakePointer>]’
/usr/local/include/tensorflow/core/framework/tensor.h:240:25: required from ‘typename tensorflow::TTypes<T>::Matrix tensorflow::Tensor::matrix() [with T = char; typename tensorflow::TTypes<T>::Matrix = Eigen::TensorMap<Eigen::Tensor<char, 2, 1, long int>, 16, Eigen::MakePointer>]’
server.cpp:121:34: required from here
/usr/local/include/tensorflow/core/framework/types.h:138:3: error: static assertion failed: Specified Data Type not supported
static_assert(IsValidDataType<T>::value, "Specified Data Type not supported");In file included from /usr/local/include/tensorflow/core/public/session.h:23:0,
from /usr/local/include/tensorflow/cc/saved_model/loader.h:26,
from server.cpp:22:
/usr/local/include/tensorflow/core/framework/tensor.h: In instantiation of ‘typename tensorflow::TTypes<T, NDIMS>::Tensor tensorflow::Tensor::tensor() [with T = char; long unsigned int NDIMS = 2ul; typename tensorflow::TTypes<T, NDIMS>::Tensor = Eigen::TensorMap<Eigen::Tensor<char, 2, 1, long int>, 16, Eigen::MakePointer>]’:
/usr/local/include/tensorflow/core/framework/tensor.h:240:25: required from ‘typename tensorflow::TTypes<T>::Matrix tensorflow::Tensor::matrix() [with T = char; typename tensorflow::TTypes<T>::Matrix = Eigen::TensorMap<Eigen::Tensor<char, 2, 1, long int>, 16, Eigen::MakePointer>]’
server.cpp:121:34: required from here
/usr/local/include/tensorflow/core/framework/tensor.h:530:46: error: ‘v’ is not a member of ‘tensorflow::DataTypeToEnum<char>’
CheckTypeAndIsAligned(DataTypeToEnum<T>::v());
Did anybody came across the same problem?
Found a solution. It seems like two issues were present.
(1) DT_UINT8 is uchar in c++
(2) use tensor instead of matrix
I changed the code to
auto x = tf::Tensor(tf::DT_UINT8, tf::TensorShape(
{height, width,3}));
auto matrix = x.tensor<uchar, 3>();
and worked. Got the idea from https://github.com/tensorflow/tensorflow/issues/19909
Even though you declare your TensorShape with an initialization list, you should still specify the number of dimensions of your matrix, as it cannot be deduced at compilation time in C++.
auto x = tf::Tensor(tf::DT_UINT8, tf::TensorShape({height, width, channel}));
auto matrix = x.matrix<char, 3>();

Function from one library matched to template from another library

I'm working on a C++ project that uses two different libraries: spdlog for logging, and mutils-serialization for serializing objects to bytes (for sending over the network). Both libraries use namespaces properly, but when I attempt to write a program that uses both of them at the same time, my compiler (g++ 6.2) gives me nonsensical errors that seem to indicate it is attempting to instantiate a function template from the spdlog library by using the definition of a function template from the mutils library.
Here's my simple test program:
#include <spdlog/spdlog.h>
#include <spdlog/fmt/ostr.h>
#include "TestSerializableObject.h"
int main(int argc, char** argv) {
auto global_logger = spdlog::rotating_logger_mt("global_logger", "log", 1024 * 1024 * 500, 3);
global_logger->set_pattern("[%H:%M:%S.%e] [%l] %v");
global_logger->set_level(spdlog::level::trace);
std::shared_ptr<spdlog::logger> logger(spdlog::get("global_logger"));
auto message = std::make_shared<messaging::TestSerializableObject>(
1, 2, "A message!");
logger->trace("Received a message: {}", *message);
}
TestSerializableObject is a simple class that implements mutils::ByteRepresentable (the interface that enables serialization and pulls in the mutils-serialization library), and provides an operator<< (which is required for spdlog to be able to log it). I can post the code for it if necessary.
When I compile this with g++ -std=c++14 -I"./src" -I"./libraries" -I"./libraries/mutils/" -L"./libraries/" -O0 -g3 -Wall "src/LibraryCollisionTest.cpp", I get this long, ugly error (don't worry, I'll help you parse it):
In file included from ./libraries/mutils/mutils.hpp:3:0,
from ./libraries/mutils-serialization/SerializationSupport.hpp:2,
from src/TestSerializableObject.h:10,
from src/LibraryCollisionTest.cpp:10:
./libraries/mutils/args-finder.hpp: In instantiation of ‘struct mutils::function_traits<messaging::TestSerializableObject>’:
./libraries/mutils/args-finder.hpp:75:41: required from ‘auto mutils::convert(F) [with F = messaging::TestSerializableObject; ignore = void]’
./libraries/spdlog/fmt/bundled/format.h:1276:46: required from ‘struct fmt::internal::ConvertToInt<messaging::TestSerializableObject>’
./libraries/spdlog/fmt/bundled/format.h:1485:5: required by substitution of ‘template<class T> fmt::internal::MakeValue<Formatter>::MakeValue(const T&, typename fmt::internal::EnableIf<fmt::internal::Not<fmt::internal::ConvertToInt<T>::value>::value, int>::type) [with T = messaging::TestSerializableObject]’
./libraries/spdlog/fmt/bundled/format.h:2465:12: required from ‘static fmt::internal::Value fmt::internal::ArgArray<N, true>::make(const T&) [with Formatter = fmt::BasicFormatter<char>; T = messaging::TestSerializableObject; unsigned int N = 1u]’
./libraries/spdlog/fmt/bundled/format.h:2898:5: required from ‘void fmt::BasicWriter<Char>::write(fmt::BasicCStringRef<CharType>, const Args& ...) [with Args = {messaging::TestSerializableObject}; Char = char]’
./libraries/spdlog/details/logger_impl.h:69:9: required from ‘void spdlog::logger::log(spdlog::level::level_enum, const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
./libraries/spdlog/details/logger_impl.h:127:5: required from ‘void spdlog::logger::trace(const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
src/LibraryCollisionTest.cpp:21:53: required from here
./libraries/mutils/args-finder.hpp:12:37: error: ‘operator()’ is not a member of ‘messaging::TestSerializableObject’
: public function_traits<decltype(&T::operator())>
^~
./libraries/mutils/args-finder.hpp: In instantiation of ‘auto mutils::convert(F) [with F = messaging::TestSerializableObject; ignore = void]’:
./libraries/spdlog/fmt/bundled/format.h:1276:46: required from ‘struct fmt::internal::ConvertToInt<messaging::TestSerializableObject>’
./libraries/spdlog/fmt/bundled/format.h:1485:5: required by substitution of ‘template<class T> fmt::internal::MakeValue<Formatter>::MakeValue(const T&, typename fmt::internal::EnableIf<fmt::internal::Not<fmt::internal::ConvertToInt<T>::value>::value, int>::type) [with T = messaging::TestSerializableObject]’
./libraries/spdlog/fmt/bundled/format.h:2465:12: required from ‘static fmt::internal::Value fmt::internal::ArgArray<N, true>::make(const T&) [with Formatter = fmt::BasicFormatter<char>; T = messaging::TestSerializableObject; unsigned int N = 1u]’
./libraries/spdlog/fmt/bundled/format.h:2898:5: required from ‘void fmt::BasicWriter<Char>::write(fmt::BasicCStringRef<CharType>, const Args& ...) [with Args = {messaging::TestSerializableObject}; Char = char]’
./libraries/spdlog/details/logger_impl.h:69:9: required from ‘void spdlog::logger::log(spdlog::level::level_enum, const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
./libraries/spdlog/details/logger_impl.h:127:5: required from ‘void spdlog::logger::trace(const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
src/LibraryCollisionTest.cpp:21:53: required from here
./libraries/mutils/args-finder.hpp:75:41: error: ‘as_function’ is not a member of ‘mutils::function_traits<messaging::TestSerializableObject>’
return function_traits<F>::as_function(f);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~
In file included from ./libraries/spdlog/fmt/fmt.h:21:0,
from ./libraries/spdlog/common.h:41,
from ./libraries/spdlog/spdlog.h:12,
from src/LibraryCollisionTest.cpp:8:
./libraries/spdlog/fmt/bundled/format.h: In instantiation of ‘struct fmt::internal::ConvertToInt<messaging::TestSerializableObject>’:
./libraries/spdlog/fmt/bundled/format.h:1485:5: required by substitution of ‘template<class T> fmt::internal::MakeValue<Formatter>::MakeValue(const T&, typename fmt::internal::EnableIf<fmt::internal::Not<fmt::internal::ConvertToInt<T>::value>::value, int>::type) [with T = messaging::TestSerializableObject]’
./libraries/spdlog/fmt/bundled/format.h:2465:12: required from ‘static fmt::internal::Value fmt::internal::ArgArray<N, true>::make(const T&) [with Formatter = fmt::BasicFormatter<char>; T = messaging::TestSerializableObject; unsigned int N = 1u]’
./libraries/spdlog/fmt/bundled/format.h:2898:5: required from ‘void fmt::BasicWriter<Char>::write(fmt::BasicCStringRef<CharType>, const Args& ...) [with Args = {messaging::TestSerializableObject}; Char = char]’
./libraries/spdlog/details/logger_impl.h:69:9: required from ‘void spdlog::logger::log(spdlog::level::level_enum, const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
./libraries/spdlog/details/logger_impl.h:127:5: required from ‘void spdlog::logger::trace(const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
src/LibraryCollisionTest.cpp:21:53: required from here
./libraries/spdlog/fmt/bundled/format.h:1276:38: warning: invalid application of ‘sizeof’ to a void type [-Wpointer-arith]
enum { enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes) };
The key line is here:
./libraries/mutils/args-finder.hpp: In instantiation of ‘auto mutils::convert(F)
[with F = messaging::TestSerializableObject; ignore = void]’:
./libraries/spdlog/fmt/bundled/format.h:1276:46: required from ‘struct
fmt::internal::ConvertToInt<messaging::TestSerializableObject>’
./libraries/spdlog/fmt/bundled/format.h:1485:5: required by substitution of
‘template<class T> fmt::internal::MakeValue<Formatter>::MakeValue(const
T&, typename fmt::internal::EnableIf<fmt::internal::Not<
fmt::internal::ConvertToInt<T>::value>::value, int>::type) [with T =
messaging::TestSerializableObject]’
Somehow, g++ has jumped from expanding a templated function inside the spdlog library, in namespace fmt::internal, to a function template in the mutils library, in namespace mutils, which is clearly not what the spdlog library intended to do! If I look at line 1276 of format.h, it's the one that calls the "convert" function inside this template struct:
template<typename T>
struct ConvertToInt
{
enum { enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes) };
enum { value = ConvertToIntImpl2<T, enable_conversion>::value };
};
A few lines above, sure enough, is the function "convert":
template <typename T>
T &get();
Yes &convert(fmt::ULongLong);
No &convert(...);
These are all inside the namespace fmt::internal, and my IDE agrees that if I want the definition of the function "convert" on line 1276, I should jump to the function "convert" on line 1248. So why does g++ ignore this definition, and instead try to use the definition for mutils::convert(), which isn't even in the right namespace?
Note that clang also fails to compile this program, and makes the same mistake, so I don't think that this is a bug in g++.
This is definitively a bug in spdlog fmtlib, used internally by spdlog.
The problem is described summarily in this FAQ:
What is “Argument-Dependent Lookup” (aka ADL, or “Koenig Lookup”)?
Because messaging::TestSerializableObject inherits from a type in namespace mutils, when convert is called unqualified from inside namespace fmt::internal with a TestSerializableObject, both fmt::internal::convert and mutils::convert are considered in the overload set. Variadic functions always rank last during overload resolution, so the template argument F in the latter is a better match than the ... in the former and mutils::convert is chosen.
This is in no way specific to your code or to mutils – any type with a unary function or function template named convert in the same namespace or a parent namespace is susceptible to this problem.
The fix is to qualify the convert call and change the definition of fmt::internal::ConvertToInt<T>::enable_conversion from
enum { enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes) };
to
enum { enable_conversion = sizeof(internal::convert(get<T>())) == sizeof(Yes) };
In my own code I make a habit of always qualifying all calls to functions inside any internal/detail namespace, even from code inside that same namespace, unless ADL usage is explicitly intended. (N.b. calls don't need to be fully qualified, merely qualified.) I learned this lesson from watching Boost have to deal with this problem the hard way as C++11 was emerging.

boost multiprecision + numeric bindings + LAPACK

I'm trying to implement some numeric procedures using the boost multiprecision template library in combination with numeric bindings for a lapack backend.
However, the template resolution doesn't seem to work as intended. I've been able to narrow it down to this minimal example:
#include <boost/numeric/ublas/symmetric.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/numeric/bindings/traits/ublas_matrix.hpp>
#include "boost/numeric/bindings/lapack/syev.hpp"
#include "boost/numeric/bindings/lapack/workspace.hpp"
typedef boost::multiprecision::number<boost::multiprecision::cpp_dec_float<100> > SuperFloat;
typedef std::numeric_limits<boost::multiprecision::cpp_dec_float<100> > SuperFloatPrecision;
typedef boost::numeric::ublas::matrix<SuperFloat> Matrix;
typedef boost::numeric::ublas::symmetric_matrix<SuperFloat> MatrixSym;
inline int diagonalize(Matrix& eigenvectors, boost::numeric::ublas::vector<SuperFloat>& eigenvalues) {
int r = boost::numeric::bindings::lapack::syev( 'U', eigenvectors, eigenvalues, boost::numeric::bindings::lapack::minimal_workspace() );
return r;
}
int main(){
std::cout << "hello!" << std::endl;
return 0;
}
which yields this error message:
boost/numeric/bindings/lapack/syev.hpp: In instantiation of ‘int boost::numeric::bindings::lapack::syev(char, A&, W&, boost::numeric::bindings::lapack::minimal_workspace) [with A = boost::numeric::ublas::matrix<boost::multiprecision::number<boost::multiprecision::backends::cpp_dec_float<100u> > >; W = boost::numeric::ublas::vector<boost::multiprecision::number<boost::multiprecision::backends::cpp_dec_float<100u> > >]’:
test.cxx:13:137: required from here
boost/numeric/bindings/lapack/syev.hpp:163:8: error: invalid application of ‘sizeof’ to incomplete type ‘boost::STATIC_ASSERTION_FAILURE<false>’
BOOST_STATIC_ASSERT( (boost::mpl::or_< boost::is_same< matrix_structure, traits::symmetric_t >
^
In file included from ./boost/numeric/bindings/traits/traits.hpp:18:0,
from ./boost/numeric/bindings/traits/ublas_matrix.hpp:17,
from test.cxx:3:
./boost/numeric/bindings/traits/matrix_traits.hpp: In instantiation of ‘char boost::numeric::bindings::traits::matrix_uplo_tag(SymmM&) [with SymmM = boost::numeric::ublas::matrix<boost::multiprecision::number<boost::multiprecision::backends::cpp_dec_float<100u> > >]’:
boost/numeric/bindings/lapack/syev.hpp:181:47: required from ‘int boost::numeric::bindings::lapack::syev(char, A&, W&, boost::numeric::bindings::lapack::minimal_workspace) [with A = boost::numeric::ublas::matrix<boost::multiprecision::number<boost::multiprecision::backends::cpp_dec_float<100u> > >; W = boost::numeric::ublas::vector<boost::multiprecision::number<boost::multiprecision::backends::cpp_dec_float<100u> > >]’
test.cxx:13:137: required from here
./boost/numeric/bindings/traits/matrix_traits.hpp:141:56: error: no type named ‘uplo_type’ in ‘struct boost::numeric::bindings::traits::matrix_traits<boost::numeric::ublas::matrix<boost::multiprecision::number<boost::multiprecision::backends::cpp_dec_float<100u> > > >’
typedef typename matrix_traits<SymmM>::uplo_type uplo_t;
Is it at all possible to use lapack as a backend for boost UBLAS templated to multiprecision, or is this a futile endeavour?
Are there any other ways to do, say, an eigenvector decomposition of a ublas multiprecision matrix?