Расширенный boost::lexical_cast для других типов данных класса

Можно ли расширить boost::lexical_cast для обработки других типов данных без фактического изменения этих классов?

В моем случае я хочу расширить его для обработки таких вещей, как cv::Point и cv::Point3, взяв список координат, разделенных строками, и загрузив их. Итак, возможность сделать что-то вроде:

cv::Point mypoint = boost::lexical_cast<cv::Point>("2,4");

В классе cv::Point уже есть потоковые операторы, но они несовместимы с istream и wstream, поэтому он не работает.

Изменить

Я спрашиваю об этом, потому что я работаю в среде с шаблонной функцией get_parameter, которая использует boost::lexical_cast для преобразования строки (прочитанной из файла конфигурации) в желаемый тип данных. Он отлично работает для целых чисел и чисел с плавающей запятой, но сейчас мне приходится вызывать его несколько раз, чтобы прочитать 2D- или 3D-точку (или, что еще хуже, массивы коэффициентов). Было бы неплохо иметь возможность модифицировать lexical_cast для обработки таких случаев.

Таким образом, это не относится к OpenCV, я просто выбрал его как самый простой тип данных. Меня больше интересует общее решение.

Изменить 2

Вот пример приложения, которое я пробовал:

#include <opencv2/opencv.hpp>
#include <boost/lexical_cast.hpp>

template <typename T>
std::istream& operator>>(std::istream& stream, cv::Point_<T> &p) {
   // Eventually something will go here
   // to put stream into p
}

int main(int argc, char **argv) {
  cv::Point_<float> p = boost::lexical_cast<cv::Point_<float>>(std::string("1,2"));
  std::cout << "p = " << p << std::endl;
  return 0;
}

И он терпит неудачу с красивой ошибкой шаблона C++, например:

In file included from /home/rhand/Development/mlx/ml_3rdparty/install/boost/include/boost/lexical_cast.hpp:41:0,
             from /home/rhand/Development/experiments/lexical_Cast/test.cc:2:
/home/rhand/Development/mlx/ml_3rdparty/install/boost/include/boost/lexical_cast.hpp: In instantiation of ‘struct boost::detail::deduce_target_char_impl<boost::detail::deduce_character_type_later<cv::Point_<float> > >’:
/home/rhand/Development/mlx/ml_3rdparty/install/boost/include/boost/lexical_cast.hpp:415:89:   required from ‘struct boost::detail::deduce_target_char<cv::Point_<float> >’
/home/rhand/Development/mlx/ml_3rdparty/install/boost/include/boost/lexical_cast.hpp:674:92:   required from ‘struct boost::detail::lexical_cast_stream_traits<std::basic_string<char>, cv::Point_<float> >’
/home/rhand/Development/mlx/ml_3rdparty/install/boost/include/boost/lexical_cast.hpp:2363:19:   required from ‘static Target boost::detail::lexical_cast_do_cast<Target, Source>::lexical_cast_impl(const Source&) [with Target = cv::Point_<float>; Source = std::basic_string<char>]’
/home/rhand/Development/mlx/ml_3rdparty/install/boost/include/boost/lexical_cast.hpp:2543:50:   required from ‘Target boost::lexical_cast(const Source&) [with Target = cv::Point_<float>; Source = std::basic_string<char>]’
/home/rhand/Development/experiments/lexical_Cast/test.cc:11:82:   required from here
/home/rhand/Development/mlx/ml_3rdparty/install/boost/include/boost/static_assert.hpp:31:45: error: static assertion failed: Target type is neither std::istream`able nor std::wistream`able
 #     define BOOST_STATIC_ASSERT_MSG( ... ) static_assert(__VA_ARGS__)
                                         ^
/home/rhand/Development/mlx/ml_3rdparty/install/boost/include/boost/lexical_cast.hpp:388:13: note: in expansion of macro ‘BOOST_STATIC_ASSERT_MSG’
         BOOST_STATIC_ASSERT_MSG((result_t::value || boost::has_right_shift<std::basic_istream<wchar_t>, T >::value),
         ^
make[2]: *** [CMakeFiles/test.dir/test.cc.o] Error 1
make[1]: *** [CMakeFiles/test.dir/all] Error 2
make: *** [all] Error 2

person Yeraze    schedule 08.10.2014    source источник
comment
Вы не можете указать свою lexical_cast специализацию?   -  person Chad    schedule 08.10.2014
comment
Почему вы предпочитаете навязывать поведение lexical_cast, а не просто создавать свободную функцию? Мне просто интересно.   -  person Nir Friedman    schedule 08.10.2014
comment
Фреймворк, в котором я работаю, имеет шаблонную функцию get_parameter, которая использует boost::lexical_cast внутри для преобразования строки (чтение из файла) в желаемый тип данных. Сейчас мне нужно вручную читать X и Y отдельно, это было бы удобно просто прочитать cv::Point.   -  person Yeraze    schedule 08.10.2014


Ответы (2)


Вы можете определить специализацию boost::lexical_cast для типов, которые вы хотите преобразовать.

Пример игрушки:

typedef struct { int x; int y; } Point;

namespace boost {
    template<>
      std::string lexical_cast(const Point& arg) { return "2,3"; }
}

int main () {
    std::cout << boost::lexical_cast<std::string>(Point ()) << std::endl;
    }

печатает 2,3.

Переход от строки к точке требует немного больше работы, но вы можете увидеть, как это сделать.

person Marshall Clow    schedule 08.10.2014

Если память не изменяет, вы сможете определить свободную функцию (istream& operator>>(istream&, cv::Point&)), которую будет использовать lexical_cast, если она определена до вызова lexical_cast.

Изменить: см. пример Перегрузка оператора istream›› c++

Редактировать 2: Вот рабочий пример (VS2013, boost 1.44 [да, мне нужно обновить!]). Не могли бы вы опубликовать минимальный пример, который терпит неудачу в вашем случае?

#include <iostream>
#include <boost/lexical_cast.hpp>

template <typename ElementType>
struct Point
{
    Point() : x(0), y(0) {}
    ElementType x, y;
};

template <typename ElementType>
std::istream& operator>>(std::istream& stream, Point<ElementType> &p)
{
    stream >> p.x;
    stream.get();
    stream >> p.y;
    return stream;
}

template <typename ElementType>
std::ostream& operator<<(std::ostream& stream, const Point<ElementType> &p)
{
    stream << p.x << "," << p.y;
    return stream;
}

int main(int argc, char **argv)
{
    Point<int> p = boost::lexical_cast<Point<int>>("1,2");
    std::cout << "p=[" << p << "]";
    std::cin.get();
    return 0;
}

Редактировать 3: сделал класс Point шаблонным, так как это похоже на ваш случай

Изменить 4: с OpenCV 2.4.10 также работает с вышеупомянутой настройкой:

#include <iostream>
#include <boost/lexical_cast.hpp>
#include <opencv2/opencv.hpp>

template <typename ElementType>
std::istream& operator>>(std::istream& stream, cv::Point_<ElementType> &p)
{
    stream >> p.x;
    stream.get();
    stream >> p.y;
    return stream;
}

int main(int argc, char **argv)
{
    auto cv_p = boost::lexical_cast<cv::Point_<float>>(std::string("1,2"));
    std::cout << "opencv p=" << cv_p << "";
    std::cin.get();
    return 0;
}
person Denys Bulant    schedule 08.10.2014
comment
хм.. Кажется, не работает, я добавил шаблон-заглушку ‹class T› cv::Point_‹T›& operator‹‹(std::istream& is, std::string &is) и шаблон-заглушку ‹class T › std::istream& operator››(std::istream& is, cv::Point_‹T› &p), и он по-прежнему жалуется, что несовместим с istream и wstream. - person Yeraze; 08.10.2014
comment
@Yeraze, кхм, извините, тогда я не помню, как правильно ... Я поищу, когда вернусь домой, если решение не будет опубликовано до тех пор. - person Denys Bulant; 08.10.2014
comment
@Yeraze Я добавил минимальный пример, но без использования точки OpenCV. Не могли бы вы проверить, работает ли этот простой случай с вашим набором инструментов? - person Denys Bulant; 08.10.2014
comment
Все еще не работает :-/ Я опубликовал свой пример сбоя и сообщение об ошибке. - person Yeraze; 09.10.2014
comment
@Yeraze Возможно, вы захотите завершить тестовый код. Особый интерес представляет тот факт, что ваш компилятор не жалуется на отсутствие возврата istream в вашей перегрузке оператора. Кроме того, вы можете попробовать мою простую версию, и только ее? - person Denys Bulant; 09.10.2014
comment
@Yeraze Это странно; даже в тестовом примере boost используется этот подход: boost.org/doc/ libs/1_55_0/libs/conversion/lexical_cast_test.cpp (void operators_overload_test()) Ваш компилятор относительно новый? - person Denys Bulant; 09.10.2014
comment
Я тоже не могу заставить вашу версию работать. Я использую gcc4.8.2 с -std=c++11 . - person Yeraze; 09.10.2014