Оцените качество кода на С++
От: GhostCoders Россия  
Дата: 18.09.14 09:43
Оценка:
Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.

// SewingDlg.h 
#pragma once

#include "MainHeaders.h"
#include "afxwin.h"

#include <vector>
#include <map>

#ifdef max
#undef max
#endif

#ifdef min
#undef min
#endif

class KompasObjectHolder
{
protected:
    reference m_reference;
public:
    KompasObjectHolder() : m_reference(0) {}
    KompasObjectHolder(reference object_reference) : m_reference(object_reference) {}
    KompasObjectHolder(KompasObjectHolder& prev_holder) : m_reference(prev_holder.m_reference) { prev_holder.m_reference = 0; }
    KompasObjectHolder& operator =(KompasObjectHolder& prev_holder) {
        if (!m_reference) m_reference = prev_holder.m_reference, prev_holder.m_reference = 0; return *this; }
    ~KompasObjectHolder()
    {
        if (m_reference) DeleteObj(m_reference);
        m_reference = 0;
    }
    operator reference() { return m_reference; }
    operator bool() { return m_reference != 0; }
};

class KompasIteratorHolder : public KompasObjectHolder
{
public:
    KompasIteratorHolder() {}
    KompasIteratorHolder(reference iterator_reference) : KompasObjectHolder(iterator_reference) {}
    KompasIteratorHolder& operator =(KompasIteratorHolder& prev_holder) {
        if (!m_reference) m_reference = prev_holder.m_reference, prev_holder.m_reference = 0; return *this; }
    ~KompasIteratorHolder()
    {
        if (m_reference) DeleteIterator(m_reference);
        m_reference = 0;
    }
};


class CSewingDlg : public CDialog       // диалоговое окно CSewingDlg
{
    DECLARE_DYNAMIC(CSewingDlg)

public:
    CSewingDlg(CWnd* pParent = NULL);   // стандартный конструктор
    virtual ~CSewingDlg();

    reference Draw2DElement(reference element, bool closed = false);
    reference DrawLineSeg(reference element);
    reference DrawCircle(reference element);
    reference DrawArc(reference element);
    reference DrawEllipse(reference element);
        reference DrawEllipseArc(reference element);                    // BAD when coercing EllipseArc & Nurbs ??bag in KOMPAS
    reference DrawEllipseArcWithNurbs(reference element);               // new API7+IMath2D version - !EllipseArc as case of Nurbs
    reference DrawRectangle(reference element);
    reference DrawRegularPolygon(reference element);
        reference DrawBezier(reference element, bool closed = false);   // new API7 version - allows Bezier 2 type (Equidistants fail too)
    reference DrawBezierWithNurbs(reference element, bool closed);      // new API7+IMath2D version - !Bezier as case of Nurbs
    reference DrawEquidistant7(IDrawingObjectPtr& idcContour,
                              EquidistantParam *equiparam, bool swapped);       // new API7 version - no adv.features
    reference DrawEquidistant(EquidistantParam *equiparam, bool swapped);       // new API5 version - ?
    reference DrawPolyline(reference element, bool closed = false);
        reference DrawApproximation(reference element, int curve_type);
    reference DrawContour(reference element, bool closed = false);
    reference DrawNurbs(reference element, bool closed = false);    // new API7+IMath2D version - !works

    reference SmallNurbs3(double x1, double y1, double x2, double y2, unsigned short style);

    double CurvesDeviation(reference old_curve, ICurve2DPtr& new_curve, double precision, double* tpars, int points_count);

    inline void KompasCorrectAngles(double& dBeginAngle, double& dEndAngle, bool clockwise);

    int PseudoProcessAcceptedData(void* pInputData, EquidistantParam *equiparam, bool PseudoGroupMode);
    void PrepareContoursMakeEquidistants(reference obj_iterator, EquidistantParam* equiparam);

    void ProcessMarks(reference curve, reference contour, EquidistantParam* equiparam);

    bool CheckSwapBegPoint(double& x1, double& y1, double& x2, double& y2);
    bool CheckTipWithScratch(double& x1, double& y1);
    bool CheckSwapCurve(reference curve);
    reference DestroyContoursGroup(int tipSearch);
    double GetEquidistantPerimeter(reference element);      // using destroyObj to get true length of Equidistant
    int CheckContourSide(reference contour, bool swapped, bool issingle);
    int CheckIntersections(reference contour, bool swapped, double t);

    bool TestInterrupt(LPSTR msg, int count);

void api7test();

    bool Activated;
    PointParam m_BegPoint;
    reference m_grpAuxScratches;
    unsigned short m_curves_style;

// Данные диалогового окна
    enum { IDD = IDD_SEWINGDLG };
    enum { enCurves_All = 0, enCurves_Selected, enCurves_Unselected };

protected:
    virtual BOOL OnInitDialog();

    void ParseDoubleData(int nID);

    virtual void DoDataExchange(CDataExchange* pDX);    // поддержка DDX/DDV

    DECLARE_MESSAGE_MAP()
public:
    afx_msg void OnBnClickedDrawOperation();
    afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);
    afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
    afx_msg void OnBnClickedCancel();
    afx_msg void OnBnClickedUndo();
    afx_msg void OnBnClickedRedo();
protected:
    int m_StockOutCheck;
    int m_StockInCheck;
    int m_MarkingCheck;
    double m_StockOut;
    double m_StockIn;
    double m_MarkingStep;
    int m_SelectType;
private:
    int m_UndoRedo;
    int m_ProcessClosedOnly;
    int m_ContourHasAnyLineStyles;
    int m_ProcessEnclosedContours;
    int m_MaxSplitNum;
    int m_MaxMarksNum;
    double m_MmToUnits;

    double m_precision;

    std::map<reference, reference> m_CurveParts;
    std::vector<reference> equi_curves;     // tmp vector needed to correct Equidistant behavior
    int equi_curves_count;                  // may differ from equi_curves.size() due to m_grpAuxScratches
    bool m_swapped;
    bool m_issingle;
    reference m_aux_scratch;
    bool m_skip_scratch;        // in API5 Contour()-mode all references are 0 = i.e. temp intrinsic objects for Contour()
public:
    afx_msg void OnBnClickedManualMarks();
    afx_msg void OnEnKillfocusPrecision();
    afx_msg void OnEnKillfocusStockout();
    afx_msg void OnEnKillfocusStockin();
    afx_msg void OnEnKillfocusMarking();
    afx_msg void OnEnChangeMarking();
};


// SewingDlg.cpp: файл реализации
//

#include "stdafx.h"
#include "SewingDlg.h"
#include "ProgressDlg.h"
#include "SewingPlugin.h"

#include "testEvents/BaseEvent.h"

#include <limits>
#include <cmath>
#include <algorithm>

const double PI2DEGREE = 180.0 / 3.14159265;
const int EQUI_STYLE = ksCSConstruction;    // 6 : вспомогательная (красная)

extern BOOL glb_Interrupt;

//   from eventsAuto.cpp
void AdviseDoc( KompasObject* kompas,
                LPDISPATCH doc, long docType,
                bool fSelectMng = true,
                bool fObject = true,
                bool fStamp = true,
                bool fDocument = true,
                bool fSpecification = true,
                bool fSpcObject = true,
                long objType = 0 /*= -1*/);

#if defined(ASSERT) && !defined(_DEBUG)
#undef ASSERT
#define FILEW2(x) L##x
#define FILEW(f) FILEW2(f)
#define ASSERT(f) {CString sss;if (!(f)){sss.Format(L"BAD ASSERT in line %d (file " FILEW(__FILE__) L")", __LINE__);MessageW((LPWSTR)sss.GetString());}}
#endif

#define MESSAGE_COMMA ,
#define MESSAGE(mesh) {   CString sss; sss.Format(mesh); MessageW((LPWSTR)sss.GetString());   }

template<typename FloatType> inline // to improve need call_traits, etc
bool approx_equals(FloatType x, FloatType y, FloatType relative_factor=FloatType(64))
{
    // default relative_factor=64 results in approximately 2 decimal digits tolerance in significand
    using std::abs; using std::max; using std::numeric_limits;
    return abs(x-y) <= ( relative_factor * numeric_limits<FloatType>::epsilon() * max(abs(x),abs(y)) );
}

static inline bool equal_points(double x1, double y1, double x2, double y2, double precision = 0.0)
{
    // double epsilon = 1e-16, so 1e9 gives real Kompas precision about 1e-7..1e-6..1e-5
    //return VTSL::Math::approx_equals<double>( x1, x2, double(1e9) ) && VTSL::Math::approx_equals<double>( y1, y2, double(1e9) );
    using std::abs; using std::max; using std::numeric_limits;

    double minerror = DBL_EPSILON * double(1e9);

    if (precision > minerror)
        return max(abs(x1 - x2), abs(y1 - y2)) <= precision;
//Message("equal_points");

//  double correction = double(1e9) * (precision <= minerror? 1.0 : precision / minerror);
    double correction = (precision <= minerror? minerror : precision) / DBL_EPSILON;
    bool bx, by;
    double absmax = max(abs(x1), abs(x2));
    if ( absmax > 1.0 ) bx = approx_equals<double>( x1, x2, correction );
    else {
        if ( absmax < minerror ) bx = true;
            else bx = approx_equals<double>( x1/absmax, x2/absmax, correction );
    }
    absmax = max(abs(y1), abs(y2));
    if ( absmax > 1.0 ) by = approx_equals<double>( y1, y2, correction );
    else {
        if ( absmax < minerror ) by = true;
            else by = approx_equals<double>( y1/absmax, y2/absmax, correction );
    }
    return bx && by;
}

static inline bool equal_values(double val1, double val2,  double precision = 0.0)
{
    const double minerror = DBL_EPSILON * double(1e9);
//  double correction = double(1e9) * (precision <= minerror? 1.0 : precision / minerror);
    double correction = (precision <= minerror? minerror : precision) / DBL_EPSILON;
    double absmax = std::max(abs(val1), abs(val2));
    if ( absmax < 1.0 )
    {
        if (absmax < minerror) return true;    // DBL_EPSILON * 64
        return approx_equals<double>( val1/absmax, val2/absmax, correction);
    }
    return approx_equals<double>( val1, val2, correction);
}

static bool IsContourInsideContour(reference contour, reference other, CSewingDlg* pThis)
{
    int type = GetObjParam(other, 0, 0, ALLPARAM);
    int side = 0;
    double t1, t2, x1, y1, x2, y2;
    ksGetCurveMinMaxParametr (contour, &t1, &t2);
    ksGetCurvePoint(contour, t1, &x1, &y1);
    ksGetCurvePoint(contour, t2, &x2, &y2);

    if (type == CONTOUR_OBJ) {
        side = ksIsPointInsideContour(other, x1, y1, 1e-6);
    }

    if (side == 0) {    // special cases for single bezier contour etc.
        reference bref = ksApproximationCurve(other, 0.1, 0, 0, 1);
        if (bref)
        {
            int type = GetObjParam(bref, 0, 0, ALLPARAM);
            if (type == CONTOUR_OBJ) {
                side = ksIsPointInsideContour(bref, x1, y1, 1e-6);
            }
            DeleteObj(bref);
        }
    }

    if (side == 0) do {    // special cases for single rectangle, circle etc.

        KompasObjectHolder decomposed_group(DecomposeObj(other, 5, 0.5, 1));
        if (!decomposed_group) break;

        KompasIteratorHolder group_iterator(CreateIterator(ALL_OBJ, decomposed_group));
        if (!group_iterator) break;

        Contour(1);

        reference cur_obj = MoveIterator(group_iterator, 'F');
        while (cur_obj)
        {
            pThis->Draw2DElement(cur_obj);
            cur_obj = MoveIterator(group_iterator, 'N');
        }

        reference contour_obj = EndObj();
        if (contour_obj)
            side = ksIsPointInsideContour(contour_obj, x1, y1, 1e-6);
        if (contour_obj) DeleteObj(contour_obj);
    } while (0);

    if (side == 3) {
Message("IsContourInsideContour");
        int kp = 0;
        IntersectCurvCurv (contour, other, &kp, 0, 0, 0);
        if (kp == 0) return true;
    }
    return false;
}

// диалоговое окно CSewingDlg

IMPLEMENT_DYNAMIC(CSewingDlg, CDialog)

CSewingDlg::CSewingDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CSewingDlg::IDD, pParent)
    , m_StockInCheck(false)
    , m_StockOutCheck(false)
    , m_MarkingCheck(false)
    , m_StockOut(0)
    , m_StockIn(0)
    , m_MarkingStep(0)
    , m_SelectType(0)
    , m_UndoRedo(0)
    , m_ProcessClosedOnly(1)
    , m_ContourHasAnyLineStyles(0)
    , m_ProcessEnclosedContours(0)      // enclosing may be too slow
    , m_MaxSplitNum(20)
    , m_MaxMarksNum(10)
    , m_precision(1.0)
{
    Activated = true;
    m_BegPoint.x = m_BegPoint.y = 0;
    m_BegPoint.style = 0;
    m_grpAuxScratches = 0; m_curves_style = 0;
    m_MmToUnits = 1.0;
    m_swapped = m_issingle = false;
    m_aux_scratch = 0; m_skip_scratch = false;
}

CSewingDlg::~CSewingDlg()
{
}

void CSewingDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_Radio(pDX,      IDC_RADIO1,             m_SelectType);
    DDX_Check(pDX,      IDC_STOCKOUT_CHECK,     m_StockOutCheck);
    DDX_Check(pDX,      IDC_STOCKIN_CHECK,      m_StockInCheck);
    DDX_Check(pDX,      IDC_MARKING_CHECK,      m_MarkingCheck);
    DDX_Check(pDX,      IDC_PROCESS_CLOSED_ONLY,            m_ProcessClosedOnly);
    DDX_Check(pDX,      IDC_CONTOUR_HAS_ANY_LINE_STYLES,    m_ContourHasAnyLineStyles);
    DDX_Check(pDX,      IDC_PROCESS_ENCLOSED_CONTUORS,      m_ProcessEnclosedContours);
    DDX_Text (pDX,      IDC_STOCKOUT,           m_StockOut);
    DDX_Text (pDX,      IDC_STOCKIN,            m_StockIn);
    DDX_Text (pDX,      IDC_MARKING,            m_MarkingStep);
    DDX_Text (pDX,      IDC_CURVE_MAX_POINTS,   m_MaxSplitNum);
    DDX_Text (pDX,      IDC_MARKS_PER_CURVE,    m_MaxMarksNum);
    DDX_Text (pDX,      IDC_PRECISION,          m_precision);
}


BEGIN_MESSAGE_MAP(CSewingDlg, CDialog)
    ON_BN_CLICKED(IDC_DRAW_OPERATION, &CSewingDlg::OnBnClickedDrawOperation)
//    ON_WM_ACTIVATE()
    ON_WM_KEYDOWN()
    ON_BN_CLICKED(IDCANCEL, &CSewingDlg::OnBnClickedCancel)
    ON_BN_CLICKED(IDC_UNDO, &CSewingDlg::OnBnClickedUndo)
    ON_BN_CLICKED(IDC_REDO, &CSewingDlg::OnBnClickedRedo)
//    ON_WM_CHAR()
ON_WM_ACTIVATE()
ON_BN_CLICKED(IDC_MANUAL_MARKS, &CSewingDlg::OnBnClickedManualMarks)
ON_EN_KILLFOCUS(IDC_PRECISION, &CSewingDlg::OnEnKillfocusPrecision)
ON_EN_KILLFOCUS(IDC_STOCKOUT, &CSewingDlg::OnEnKillfocusStockout)
ON_EN_KILLFOCUS(IDC_STOCKIN, &CSewingDlg::OnEnKillfocusStockin)
ON_EN_KILLFOCUS(IDC_MARKING, &CSewingDlg::OnEnKillfocusMarking)
ON_EN_CHANGE(IDC_MARKING, &CSewingDlg::OnEnChangeMarking)
END_MESSAGE_MAP()

//-----------------------------------------------------------------------------
// Dialog initialization
BOOL CSewingDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    m_StockOutCheck = true;
    m_StockInCheck = false;
    m_MarkingCheck = true;
    m_StockOut = 10.50;
    m_StockIn  = 10.50;
    m_MarkingStep = 100;
    m_SelectType = 0;
    m_MaxSplitNum = 20;
    m_MaxMarksNum = 5;

    ((CButton*)GetDlgItem( IDC_PROCESS_CLOSED_ONLY ))->SetCheck( m_ProcessClosedOnly? 1 : 0 );
    ((CButton*)GetDlgItem( IDC_CONTOUR_HAS_ANY_LINE_STYLES ))->SetCheck( m_ContourHasAnyLineStyles? 1 : 0 );
    ((CButton*)GetDlgItem( IDC_PROCESS_ENCLOSED_CONTUORS ))->SetCheck( m_ProcessEnclosedContours? 1 : 0 );

    UpdateData(FALSE);

    return TRUE;  // return TRUE unless you set the focus to a control
                  // EXCEPTION: OCX Property Pages should return FALSE
}


/*
    //reference obj = Circle(100,100,100,1);
/ *
    RectangleParam par; // структура параметров прямоугольника
    ::memset(&par, 0, sizeof(RectangleParam));

    par.x = -73.55; // базовая точка 1
    par.y = 39.95; // 
    par.ang = 0.00; // угол вектора направления от 1-ой точки ко 2-ой
    par.height = -66.68; // высота
    par.width = 79.90; // ширина
    par.pCorner = ::CreateArray(CORNER_ARR, 0); // Создание массива параметров углов
    par.style = 1; // стиль линии

    reference obj = ::ksRectangle(&par, 0); // создать прямоугольник 
* /
/ *
    reference rseg1 = LineSeg(10,10,100,100,1);
    reference rseg2 = LineSeg(100,100,200,20,1);

    reference obj = NewGroup(0);
    AddObjGroup(obj, rseg1);
    AddObjGroup(obj, rseg2);
    EndGroup();* /

    Contour(1);

    //определим группу выделения
//    reference obj0 = SelectGroup(0,2,0,0,0,0); //ksViewGetObjectArea();
    //CopyObj(obj0, 0,0,0,0,1,1);
    //CopyGroupToDocument(obj0, ksGetCurrentDocument(0), ksGetCurrentDocument(0));

//    reference group_iterator = CreateIterator(LINESEG_OBJ, 0);
    reference group_iterator = CreateIterator(SELECT_GROUP_OBJ, 0);

    reference cur_obj = MoveIterator(group_iterator, 'F');
    while (cur_obj)
    {
        ksLineSegParamPtr lspLineParam = kompasAPI->GetParamStruct(ko_LineSegParam);
//        long ksres = sewing_object->m_pKompasDoc5->ksGetObjParam(cur_obj, lspLineParam, ALLPARAM);
        long ksres = m_pKompasDoc5->ksGetObjParam(cur_obj, lspLineParam, ALLPARAM);
        if(ksres)
        {
            double x1 = lspLineParam->x1,
                   x2 = lspLineParam->x2,
                   y1 = lspLineParam->y1,
                   y2 = lspLineParam->y2;

//            LineSeg(x1,y1,x2,y2,1);
/ * задание параметров копирования * /
/ *
RequestInfo info;
memset(&info, 0, sizeof(info));
info.title = "Title";
info.prompt = " Новое положение базовой точки ";
info.dynamic = 0;
info.commands = "!test!test1";
int j=CommandWindow(&info);

double x=0,y=0,scale=1,ang=0;
if ((Cursor (&info, &x, &y, 0)) &&
(ReadDouble("Угол поворота", 0, 0, 360, &ang)) &&
(ReadDouble("Масштаб", 1, 0, 999, &scale)))
;* /
/ *
reference posLeader = ksCreateViewObject(POSLEADER_OBJ );
if (posLeader){ 
LightObj(posLeader, 1); 
Message("Позиционная линия выноски");
LightObj(posLeader, 0);

    if (ksEditViewObject(posLeader)) { 
    LightObj(posLeader, 1);
    Message("Отредактированная позиционная линия выноски"); 
    LightObj(posLeader, 0);
    } 

} 
* /
//CopyObj(0, 0, 0, x, y, ang, scale); / * копирование группы gr * /
//ksCopyObj(0, 0, 0, x, y, ang, scale); / * копирование группы gr * /
            LineSeg(x1,y1,x2,y2,1);
*/


reference CSewingDlg::Draw2DElement(reference element, bool closed)
{
    if (ReturnResult()) ResultNULL();
    if (!ExistObj(element)) return 0;

    switch (GetObjParam(element, 0, 0, ALLPARAM))
    {
    case CONTOUR_OBJ:
        return DrawContour(element);        // ONLY APPROXIMATION NOW
    case LINESEG_OBJ:
        return DrawLineSeg(element);
    case CIRCLE_OBJ:
        return DrawCircle(element);
    case ELLIPSE_OBJ:
        return DrawEllipse(element);
    case ARC_OBJ:
        return DrawArc(element);
    case ELLIPSE_ARC_OBJ:
        //return DrawEllipseArcWithNurbs(element);
        return DrawEllipseArc(element);    // NO APPROXIMATION possible - it approximates into itself!
    case RECTANGLE_OBJ:
        return DrawRectangle(element);
    case REGULARPOLYGON_OBJ:
        return DrawRegularPolygon(element);

    case BEZIER_OBJ:
        return DrawBezierWithNurbs(element, closed);
        //return DrawBezier(element, closed);
    case POLYLINE_OBJ:
        return DrawPolyline(element, closed);
    case NURBS_OBJ:
        return DrawNurbs(element, closed);
    default:
        break;
    }
    return 0;
}

reference CSewingDlg::SmallNurbs3(double x1, double y1, double x2, double y2, unsigned short style)
{
//Message("SmallNurbs3");
    reference new_curve = 0;

    do {
        const int degree = 3 + 0;       // NURBS-3 needs 3 points
        bool closed = false;

        NurbsPointParam par;
        Nurbs(degree, closed, style);

        std::vector<double> Points;
        //std::vector<double> Weights;
        //std::vector<double> Knots;

        Points.push_back(x1), Points.push_back(y1);
        Points.push_back((x1 + x2) / 2), Points.push_back((y1 + y2) / 2);
        Points.push_back(x2), Points.push_back(y2);

        int basepoint_count = Points.size();

        for (int basepoint = 0; basepoint < basepoint_count; basepoint += 2)
        {
            par.x = Points[basepoint];
            par.y = Points[basepoint + 1];
            par.weight = 1.0;
            NurbsPoint(&par);
        }
        int knot_count = basepoint_count / 2 + degree;
        for (int knot = 0; knot < knot_count; knot++)
        {
            double knotdata = double(knot / degree);    // ONLY for 3 Points on 3 degree
            ksNurbsKnot(knotdata);
        }
        new_curve = EndObj();
        if (new_curve) return new_curve;    // => new_curve may be 0! like for Bezier !?
    } while (0);

    return 0;
}

bool CSewingDlg::CheckTipWithScratch(double& x1, double& y1)
{
    if (equal_points(x1,y1, m_BegPoint.x, m_BegPoint.y, m_precision * m_MmToUnits)) {
        //if (equal_points(x1,y1, m_BegPoint.x, m_BegPoint.y) == false) {
      if (m_skip_scratch == false)
        if ( ksEqualPoints(x1, y1, m_BegPoint.x, m_BegPoint.y) == 0 ) {
            reference aux_scratch = LineSeg(m_BegPoint.x, m_BegPoint.y, x1, y1, m_curves_style);
            if (aux_scratch == 0) aux_scratch = SmallNurbs3(m_BegPoint.x, m_BegPoint.y, x1, y1, m_curves_style);
            if (aux_scratch) AddObjGroup(m_grpAuxScratches, aux_scratch);
if (aux_scratch == 0)
Message("CheckSwapBegPoint - BAD scratch!");
//else Message("Tip drawn");
            m_aux_scratch = aux_scratch;
        }
        return true;
    }
    return false;
}

bool CSewingDlg::CheckSwapBegPoint(double& x1, double& y1, double& x2, double& y2)
{
    if (CheckTipWithScratch(x1, y1)) {
        m_BegPoint.x = x2, m_BegPoint.y = y2;
        return false;
    }

    if (CheckTipWithScratch(x2, y2)) {
//Message("CheckSwapBegPoint - SWAPPED!");
        m_BegPoint.x = x1, m_BegPoint.y = y1;
        x1 = x2, y1 = y2;
        x2 = m_BegPoint.x, y2 = m_BegPoint.y;
        return true;
    }
    return false;
}

bool CSewingDlg::CheckSwapCurve(reference curve)
{
    double t1, t2;
    double x1, y1, x2, y2;
    ksGetCurveMinMaxParametr (curve, &t1, &t2);
    ksGetCurvePoint(curve, t1, &x1, &y1);
    ksGetCurvePoint(curve, t2, &x2, &y2);

    //return (equal_points(x1,y1, m_BegPoint.x, m_BegPoint.y, m_precision * m_MmToUnits) == false);
    return ( ksEqualPoints(x1, y1, m_BegPoint.x, m_BegPoint.y) == 0 );
}

bool CSewingDlg::TestInterrupt(LPSTR msg, int count)
{
    if (glb_Interrupt && YesNo(msg) == 1) return true;
//Message("TestInterrupt");
//    HWND hwnd = ::SetActiveWindow((HWND) GetHWindow());
//    PumpWaitingMessages();
    ksSetProgressBar(count, "Отрисовка эквидистант...", 1);
    if (progress_object) {
        ((CProgressCtrl*)progress_object->GetDlgItem( IDC_PROGRESS ))->SetPos(count);
        if (((CProgressCtrl*)progress_object->GetDlgItem( IDCANCEL ))->IsWindowEnabled() == FALSE) {
            ((CProgressCtrl*)progress_object->GetDlgItem( IDCANCEL ))->EnableWindow(TRUE);
            ((CProgressCtrl*)progress_object->GetDlgItem( IDCANCEL ))->SetWindowTextW(L"Отмена");
        }
    }
    glb_Interrupt = FALSE;
//    ::SetActiveWindow(hwnd);
    PumpWaitingMessages();
    return false;
}

void CSewingDlg::KompasCorrectAngles(double& dBeginAngle, double& dEndAngle, bool clockwise)
{
    // KOMPAS angles sometimes need correction!:
    if (clockwise) {
        if (dBeginAngle - dEndAngle > 360) dBeginAngle -= 360 * floor((dBeginAngle - dEndAngle) / 360.0);
    } else {
        if (dEndAngle - dBeginAngle > 360) dEndAngle -= 360 * floor((dEndAngle - dBeginAngle) / 360.0);
    }

    if (dBeginAngle < 0.0 || dEndAngle < 0.0) {
        dBeginAngle = dBeginAngle - 360 * floor(dBeginAngle / 360.0);
        dEndAngle = dEndAngle - 360 * floor(dEndAngle / 360.0);
    }
    // Kompas works with angles from 0 to 360 degrees, so may be arcs par ex. from 315 to 45 degrees. DrawArc needs starting angle be less than the ending
    if (clockwise) {
        if (dEndAngle > dBeginAngle) if (dEndAngle < 360) dBeginAngle += 360; else dEndAngle -= 360;
    } else {
        if (dEndAngle < dBeginAngle) if (dBeginAngle < 360) dEndAngle += 360; else dBeginAngle -= 360;
    }
}

reference CSewingDlg::DrawLineSeg(reference element)
{
    //?if ( m_PseudoObjectsMode ) return DrawObjectsAsPseudoVector(element);

    ksLineSegParamPtr lspLineParam = kompasAPI->GetParamStruct(ko_LineSegParam);
    ksDocument2DPtr m_pKompasDoc5  = kompasAPI->ActiveDocument2D();
    long ksres = m_pKompasDoc5->ksGetObjParam(element, lspLineParam, ALLPARAM);
    if(ksres)
    {
        double x1 = lspLineParam->x1,
               x2 = lspLineParam->x2,
               y1 = lspLineParam->y1,
               y2 = lspLineParam->y2;

        CheckSwapBegPoint(x1, y1, x2, y2);

        const double perimeter =  ksGetCurvePerimeter (element, ST_MIX_MM);

        return LineSeg(x1, y1, x2, y2, 1);
    }
    return 0;
}

reference CSewingDlg::DrawCircle(reference element)
{
    CircleParam cpCircleParam;
    if (GetObjParam(element, &cpCircleParam, sizeof(CircleParam), ALLPARAM))
    {
        double x = cpCircleParam.xc,
               y = cpCircleParam.yc;

        const double perimeter =  ksGetCurvePerimeter (element, ST_MIX_MM);

        return Circle(x, y, cpCircleParam.rad, 1);
    }
    return 0;
}

reference CSewingDlg::DrawArc(reference element)
{
//Message("DrawArc");
    ArcParam apArcParam;
    ArcParam1 apArcParam1;
    if (GetObjParam(element, &apArcParam, sizeof(ArcParam), ALLPARAM)
        && GetObjParam(element, &apArcParam1, sizeof(ArcParam1), POINT_ARC_PARAM))
    {
        double x = apArcParam.xc,
               y = apArcParam.yc;

        double dBeginAngle = apArcParam.ang1,   // as if Arc is drawed counterclockwise
               dEndAngle   = apArcParam.ang2;

        double x1 = apArcParam1.x1, y1 = apArcParam1.y1;
        double x2 = apArcParam1.x2, y2 = apArcParam1.y2;
        bool clockwise = (apArcParam.dir != 1);

        if ( CheckSwapBegPoint(x1, y1, x2, y2) ) {
            clockwise = !clockwise;
            double tmpang = dBeginAngle;
            dBeginAngle = dEndAngle; dEndAngle = tmpang;
        }

        //KompasCorrectAngles(dBeginAngle, dEndAngle, clockwise);   ?? this correction not liked by Equidistant!

        double perimeter =  ksGetCurvePerimeter (element, ST_MIX_MM);

        return ArcByAngle(x, y, apArcParam.rad, dBeginAngle, dEndAngle, clockwise? -1 : 1, 1);
    }
    return 0;
}

reference CSewingDlg::DrawEllipse(reference element)
{
    EllipseParam epEllipseParam;
    if (GetObjParam(element, &epEllipseParam, sizeof(EllipseParam), ALLPARAM))
    {
        double x = epEllipseParam.xc,
               y = epEllipseParam.yc;
        double angle = epEllipseParam.ang;
        double a = epEllipseParam.a;
        double b = epEllipseParam.b;

        const double perimeter =  ksGetCurvePerimeter (element, ST_MIX_MM);

        epEllipseParam.style = 1;

        return ksEllipse(&epEllipseParam);
    }
    return 0;
}

reference CSewingDlg::DrawEllipseArcWithNurbs(reference element)
{
Message("DrawEllipseArcWithNurbs");
    return 0;
}

reference CSewingDlg::DrawEllipseArc(reference element)     // old - BAD in test with Equidistant
{
//    return 0;   // IMPOSSIBLE to make approximation! (NO WAY TOO to coerce EllipsArc & Nurbes in Equidistant)

//Message("DrawEllipseArc");
    EllipseArcParam eapEllipseArcParam;
    EllipseArcParam1 eapEllipseArcParam1;
    if (GetObjParam(element, &eapEllipseArcParam, sizeof(EllipseArcParam), ALLPARAM) &&
        GetObjParam(element, &eapEllipseArcParam1, sizeof(EllipseArcParam), POINT_ARC_PARAM))
    {
        double x = eapEllipseArcParam.xc,
               y = eapEllipseArcParam.yc;
        double angle = eapEllipseArcParam.ang;
        double ang1 = eapEllipseArcParam.angFirst;
        double ang2 = eapEllipseArcParam.angSecond;
        double a = eapEllipseArcParam.a;
        double b = eapEllipseArcParam.b;
        int dir = eapEllipseArcParam.dir;

        eapEllipseArcParam.style = eapEllipseArcParam1.style = 1;

        ASSERT(eapEllipseArcParam1.a == a && eapEllipseArcParam1.b == b)
        ASSERT(eapEllipseArcParam1.ang == angle)
        ASSERT(eapEllipseArcParam1.xc == x && eapEllipseArcParam1.yc == y)
        ASSERT(eapEllipseArcParam1.dir == dir)

        double t1, t2, t2eps;
        double x1, y1, x2, y2, x1eps, y1eps, x2eps, y2eps;
        ksGetCurveMinMaxParametr (element, &t1, &t2);

        if (dir == 1)
        ASSERT(t2 - t1 == eapEllipseArcParam1.parSecond - eapEllipseArcParam1.parFirst)
        else
        ASSERT(t1 - t2 == eapEllipseArcParam1.parSecond - eapEllipseArcParam1.parFirst)

        //ASSERT(t1 == eapEllipseArcParam.parFirst && t2 == eapEllipseArcParam.parSecond)
        //! t1 != eapEllipseArcParam.parFirst even though  (t2 - t1) == (eapEllipseArcParam.parSecond - eapEllipseArcParam.parFirst)!
        //t1 = eapEllipseArcParam.parFirst;           // or just: ksGetCurveMinMaxParametr (element, &t1, &t2);
        //t2 = eapEllipseArcParam.parSecond;
        ksGetCurvePoint(element, t1, &x1, &y1);     //? BAD instead of t1: (dir == 1? t1 : t2)!    (by the way, for contours HERE dir==1 means CLOCKWISE!?)
        ksGetCurvePoint(element, t2, &x2, &y2);

        double tstep = (t2 - t1) * 0.01  * 0;

        ksGetCurvePoint(element, t1 + tstep, &x1eps, &y1eps);
        ksGetCurvePoint(element, t2 - tstep, &x2eps, &y2eps);
        eapEllipseArcParam1.parFirst = eapEllipseArcParam1.parFirst + tstep;
        eapEllipseArcParam1.parSecond = eapEllipseArcParam1.parSecond - tstep;

        if ( CheckSwapBegPoint(x1, y1, x2, y2) ) {
            eapEllipseArcParam.dir = - eapEllipseArcParam.dir;
            double tmpang = eapEllipseArcParam.angFirst;
            eapEllipseArcParam.angFirst = eapEllipseArcParam.angSecond;
            eapEllipseArcParam.angSecond = tmpang;

            eapEllipseArcParam1.dir = eapEllipseArcParam.dir;

            double tmppar = eapEllipseArcParam1.parFirst;
            eapEllipseArcParam1.parFirst = eapEllipseArcParam1.parSecond;
            eapEllipseArcParam1.parSecond = tmppar;
        }

        double perimeter =  ksGetCurvePerimeter (element, ST_MIX_MM);

//        reference aux_scratch = LineSeg(x1, y1, x1eps, y1eps, m_curves_style);
//        if (aux_scratch) AddObjGroup(m_grpAuxScratches, aux_scratch);

        //reference ref = ksEllipseArc(&eapEllipseArcParam);
        reference ref = ksParEllipseArc(&eapEllipseArcParam1);

        // Equidistant stops after EllipseArc when Nurbs is the next.
        // This is workaround - drawing a scratch (LineSeg works fine - BUT must be called near Equidistant?!)
//        aux_scratch = LineSeg(x2eps, y2eps, m_BegPoint.x, m_BegPoint.y, m_curves_style);
//        if (aux_scratch) AddObjGroup(m_grpAuxScratches, aux_scratch);

        return ref;
    }
    return 0;
}

double CSewingDlg::GetEquidistantPerimeter(reference element)      // using destroyObj to get true length of Equidistant
{
    int type = GetObjParam(element, 0, 0, ALLPARAM);
    if (type != EQUID_OBJ) return 0.0;

    double t1, t2;
    double x1, y1, x2, y2;
    ksGetCurveMinMaxParametr (element, &t1, &t2);
    ksGetCurvePoint(element, t1, &x1, &y1);
    ksGetCurvePoint(element, t2, &x2, &y2);
    double perimeter = 0.0;     // ksDistancePntPntOnCurve(element, x1, y1, x2, y2);

    ksDocument2DPtr m_pKompasDoc5 = kompasAPI->ActiveDocument2D();  // Current Kompas document       (for KAPI5 usage)

    IKompasDocumentPtr doc7 = newKompasAPI->ActiveDocument; // ksTransferReference( m_pKompasDoc5->reference, 0 );
    if (!doc7) { Message("No document open - approx"); return 0; }

    reference a_group = NewGroup(1);
    EndGroup();

    if (a_group && AddObjGroup(a_group, element) )
    {
        // Creating doc for building of nurbs approximation
        DocumentParamT parDocument;
        memset( &parDocument, 0, sizeof( parDocument ) );
        parDocument.regim = 1;  // hidden mode
        parDocument.type = 3;
        reference tmp_doc = CreateDocumentT( &parDocument ); // fragment creation

        reference b_group = CopyGroupToDocument(a_group, m_pKompasDoc5->reference, tmp_doc);

        StoreTmpGroup( b_group );

        if (ExistGroupObj(b_group)) {

            reference elem = 0;
            do {    // pseudo-cycle to destruct tempRasters BEFORE doc-switching & CloseDocument()
                KompasIteratorHolder tempApprox(CreateIterator(ALL_OBJ, 0));    // GetViewReference(0)));
                if(!tempApprox) break;
                elem = MoveIterator(tempApprox, 'F');
            } while (0);

            ksDestroyObjects(elem);
            if (b_group) DeleteObj(b_group);
        }

        reference c_group = NewGroup(1);
        EndGroup();
        SelectGroup(c_group, 2, 0,0,0,0);

        reference n_group = 0;
        if (ExistGroupObj(c_group))
        {
            KompasIteratorHolder contour_iterator(CreateIterator(ALL_OBJ, c_group));

            reference cur_obj_reference = MoveIterator(contour_iterator, 'F');
            while (cur_obj_reference)
            {
                int type = GetObjParam(cur_obj_reference, 0, 0, ALLPARAM);
                perimeter += ksGetCurvePerimeter (cur_obj_reference, ST_MIX_MM);
                cur_obj_reference = MoveIterator(contour_iterator, 'N');
            }
        }
        if (c_group) DeleteObj(c_group);

        //doc7->Active = TRUE;
        doc7->Application->ActiveDocument = doc7;

        if (tmp_doc) CloseDocument(tmp_doc);
    }
    return perimeter;
}

reference CSewingDlg::DrawEquidistant(EquidistantParam *equiparam, bool swapped)                  // new API5 version
{
Message("DrawEquidistant");
    return 0;
}

reference CSewingDlg::DrawEquidistant7(IDrawingObjectPtr& idcContour, EquidistantParam *equiparam, bool swapped)    // new API7 version
{
//return Equidistant(equiparam);
Message("DrawEquidistant7 -> ");

    if (idcContour == 0) return 0;

    IKompasDocumentPtr doc7 = newKompasAPI->ActiveDocument; // ksTransferReference( m_pKompasDoc5->reference, 0 );
    IKompasDocument2DPtr pkdDocument = 0;
    doc7->QueryInterface(&pkdDocument);

    IViewPtr ivpView = pkdDocument->ViewsAndLayersManager->Views->View[0];
    IDrawingContainerPtr pdcContainer = 0;
    ivpView->QueryInterface(&pdcContainer);

    IDrawingObjectPtr pdoDrawingObject = 0;
    ivpView->QueryInterface(&pdoDrawingObject);

    IEquidistantsPtr Equidistant_pool = pdcContainer->Equidistants;
    IEquidistantPtr Equidistant_curve = Equidistant_pool->Add();

    IDrawingObjectPtr idoDrawingContour = idcContour;
    reference base_object = idoDrawingContour->Reference;
    //reference base_object = equiparam->geoObj;
    //IDrawingObjectPtr drawing_object = ksTransferReference( base_object, 0 );

    bool bad_state = false;

    int type = idoDrawingContour->Type;
    if (type == ksObjectDrawingContour)
    {
        IContourPtr icontour = idoDrawingContour;   // ksTransferReference(p_equiparam->geoObj, 0);
        if (icontour) {
            int icount = icontour->Count;
            if (icount < equi_curves_count) bad_state = true;
if (bad_state)
Message("DrawEquidistant7: ERROR building contour!");
//            if (bad_state == false && ref5 && (m_issingle ||
//                (ksIsCurveClosed(ref5) && ksIsCurveClosed(p_equiparam->geoObj)) )) return ref5;
        }
    }

    BOOL isok = false;
    BOOL test_isok = false;
    Equidistant_curve->DegenerateSegment = equiparam->degState == 0? FALSE : TRUE;
    do {
        Equidistant_curve->BaseObject = idoDrawingContour;
        Equidistant_curve->CutMode = equiparam->cutMode == 0? FALSE : TRUE;
        Equidistant_curve->LeftRadius = equiparam->radLeft;
        Equidistant_curve->RightRadius = equiparam->radRight;
        Equidistant_curve->Side = equiparam->side == 2? ksETBoth :
            equiparam->side == 0? ksETLeft : equiparam->side == 1? ksETRight : ksETUnknown;
        Equidistant_curve->Style = equiparam->style;        // = ksCSConstruction; // = 6

        isok = Equidistant_curve->Update();

    //pdoDrawingObject->LayerNumber = pdoDrawingObject->LayerNumber;
    //if (!pdoDrawingObject->Update()) Message("NO update");

        if (!isok) {
            Equidistant_curve->DegenerateSegment = TRUE;
            //Equidistant_curve->CutMode = TRUE;
        }
        isok = isok || test_isok;
        test_isok = true;
    } while (!isok);

    reference ref = Equidistant_curve->Reference;

    //if (type == ksObjectDrawingContour || m_issingle)
    if (m_issingle)
    {
        if (bad_state == false && ref && (m_issingle || ksIsCurveClosed(ref))) return ref;
    }

    double radWorked = Equidistant_curve->Side == ksETLeft? Equidistant_curve->LeftRadius :
        Equidistant_curve->Side == ksETRight? Equidistant_curve->RightRadius :
        std::max(Equidistant_curve->LeftRadius, Equidistant_curve->RightRadius);

//Message("BUILD equidistant -> ");
    if (isok && ref != 0 && bad_state == false) {
        double perimeter = ksGetCurvePerimeter (ref, ST_MIX_MM);   // always 0!
        double summed_len = 0.0;
        if (perimeter == 0)
            perimeter = GetEquidistantPerimeter(ref);
        for (int i = 0; i < equi_curves.size(); i++)
            summed_len += ksGetCurvePerimeter (equi_curves[i], ST_MIX_MM);
    // may be stuff dealing with wrong perimeter...
        if (Equidistant_curve->Side != ksETBoth)
            bad_state = std::abs(perimeter - summed_len) > equi_curves_count * (2.*3.20) * radWorked;   //about 2*pi
        else    // ksETBoth
            bad_state = std::abs(perimeter - 2 * summed_len) > 2. * 1 * (2.*3.20) * radWorked;
            //bad_state = std::abs(perimeter - 2 * summed_len) > 2. * equi_curves_count * (2.*3.20) * radWorked;
    }

    // API7 version writes Equidistant into object level! (as it looks?)
    if (bad_state)      // redrawing separately
    {
        //if (Equidistant_curve) Equidistant_curve->Delete();       //? KEEP it for detecting Contours nesting...
        if (Equidistant_curve && ref) AddObjGroup(m_grpAuxScratches, ref);

        double t1, t2;
        double x1, y1, x2, y2;
        ksGetCurveMinMaxParametr (equi_curves[0], &t1, &t2);
        ksGetCurvePoint(equi_curves[0], t1, &x1, &y1);
        ksGetCurvePoint(equi_curves[0], t2, &x2, &y2);
        m_BegPoint.x = x1, m_BegPoint.y = y1;
        if (swapped) m_BegPoint.x = x2, m_BegPoint.y = y2;
        reference ret_ref = 0;
        m_skip_scratch = true;
        for (int i = 0; i < equi_curves.size(); i++)
        {
            //m_pKompasDoc5->ksContour(1);
            //Contour(1);
            reference obj = 0;
            obj = Draw2DElement(equi_curves[i]);
            //reference ref5 = m_pKompasDoc5->ksEndObj();
            IDrawingObjectPtr idoObject = obj? ksTransferReference(obj, 0) : 0;
            if (obj && idoObject) {
                IEquidistantPtr Equid_curve = Equidistant_pool->Add();

                Equid_curve->BaseObject = idoObject;
                Equid_curve->CutMode = equiparam->cutMode == 0? FALSE : TRUE;
                Equid_curve->LeftRadius = equiparam->radLeft;
                Equid_curve->RightRadius = equiparam->radRight;
                Equid_curve->Side = equiparam->side == 2? ksETBoth :
                    equiparam->side == 0? ksETLeft : equiparam->side == 1? ksETRight : ksETUnknown;
                Equid_curve->Style = equiparam->style;        // = ksCSConstruction; // = 6

                Equid_curve->DegenerateSegment = equiparam->degState == 0? FALSE : TRUE;
                isok = Equid_curve->Update();
                if (isok == false) {
                    Equid_curve->BaseObject = idoObject;
                    Equid_curve->CutMode = equiparam->cutMode == 0? FALSE : TRUE;
                    Equid_curve->LeftRadius = equiparam->radLeft;
                    Equid_curve->RightRadius = equiparam->radRight;
                    Equid_curve->Side = equiparam->side == 2? ksETBoth :
                        equiparam->side == 0? ksETLeft : equiparam->side == 1? ksETRight : ksETUnknown;
                    Equid_curve->Style = equiparam->style;        // = ksCSConstruction; // = 6

                    Equid_curve->DegenerateSegment = TRUE;
                    isok = Equid_curve->Update();
                }
                ref = Equid_curve->Reference;
                DeleteObj(obj);
                if (ref == 0) Equid_curve->Delete();
            }
            if (ret_ref == 0 && ref) ret_ref = ref;
        }
        m_skip_scratch = false;
        return ret_ref;
    }

    return ref;         //? if (!ref) Message("ref == 0"); => ref may be 0!
}

reference CSewingDlg::DrawBezierWithNurbs(reference element, bool closed)        // new API7 version
{
#if defined(DO_DEBUG)
//Message("DrawBezierWithNurbs");
#endif
    IBezierPtr pBezier = ksTransferReference(element, 0);
    if (!pBezier) return 0;

/*    VARIANT varAllPoints;
    VariantInit(&varAllPoints);
    varAllPoints = pBezier->GetPoints(TRUE);
    V_VT(&varAllPoints) = VT_ARRAY | VT_R8;*/

    //AllPoints = pBezier->GetPoints(AllPoints);
    //VARIANT_BOOL boo = AllPoints.boolVal;

    BezierParam bpBezierParam;
    if (GetObjParam(element, &bpBezierParam, sizeof(BezierParam), ALLPARAM))
    {
        const int basepoint_count = GetArrayCount(bpBezierParam.pMathPoint);
        if (bpBezierParam.pMathPoint == 0 || basepoint_count == 0) return 0;

        ASSERT(pBezier->PointsCount == basepoint_count)

        std::vector<double> old_Points;

        MathPointParam mpMathPoint;
        for (int i = 0; i < basepoint_count; i++) {
            GetArrayItem(bpBezierParam.pMathPoint, i, &mpMathPoint, sizeof(MathPointParam));
            old_Points.push_back(mpMathPoint.x);
            old_Points.push_back(mpMathPoint.y);
        }

        GetArrayItem(bpBezierParam.pMathPoint, 0, &mpMathPoint, sizeof(MathPointParam));
        double x = mpMathPoint.x,
               y = mpMathPoint.y;
        double xn, yn;
        bool swapped = false;

        const double perimeter =  ksGetCurvePerimeter (element, ST_MIX_MM);
        const bool closed_path = (bpBezierParam.closed != 0 && closed);

        if (!closed_path) {
            GetArrayItem(bpBezierParam.pMathPoint, basepoint_count - 1, &mpMathPoint, sizeof(MathPointParam));
            xn = mpMathPoint.x, yn = mpMathPoint.y;
            swapped = CheckSwapBegPoint(x, y, xn, yn);
        }

        const int degree = 3 + 1;

        NurbsPointParam npPoint;

        //double base1X, base1Y, left1X, left1Y, right1X, right1Y;
        double base2X, base2Y, left2X, left2Y, right2X, right2Y, Weight;

        //BezierPointParam par;
        //Bezier(closed, 1);
        //_Bezier() may be better?

        int step = 1;
        int split_number = std::max(m_MaxSplitNum, degree);

        IMath2DPtr mathp = newKompasAPI->Application->Math2D;

        int real_count = 0;
        std::vector<double> Points;
        std::vector<double> Weights;
        std::vector<double> Knots;

        //SAFEARRAY * pSrc = V_ARRAY(&varAllPoints);

        do {
            if (basepoint_count > split_number) step = basepoint_count / split_number;

            NurbsPointParam par;

//swapped = false;
            if (swapped == false || closed_path)
            {
                for (int basepoint = 0; basepoint < basepoint_count + step-1; basepoint += step)
                {
                    if (basepoint >= basepoint_count) basepoint = basepoint_count - 1;
                    GetArrayItem(bpBezierParam.pMathPoint, basepoint, &mpMathPoint, sizeof(MathPointParam));

                    // this works too!
                    pBezier->GetPoint(basepoint,   &base2X, &base2Y, &left2X, &left2Y, &right2X, &right2Y);
/*
                    LONG i = 6 * basepoint + 0, j = 6 * basepoint + 1;      // B,L,R -> L,B,R !
                    ::SafeArrayGetElement( pSrc, &i, &left2X);
                    ::SafeArrayGetElement( pSrc, &j, &left2Y);
                    i += 2, j += 2;
                    ::SafeArrayGetElement( pSrc, &i, &base2X);
                    ::SafeArrayGetElement( pSrc, &j, &base2Y);
                    i += 2, j += 2;
                    ::SafeArrayGetElement( pSrc, &i, &right2X);
                    ::SafeArrayGetElement( pSrc, &j, &right2Y);
*/
                    ASSERT(mpMathPoint.x == base2X && mpMathPoint.y == base2Y)

                    //real_count++;
                    if (basepoint == 0)
                        Points.push_back(mpMathPoint.x), Points.push_back(mpMathPoint.y),
                        Points.push_back(right2X), Points.push_back(right2Y);
                    else if (basepoint == basepoint_count - 1 && closed_path == false)
                        Points.push_back(left2X), Points.push_back(left2Y),
                        Points.push_back(mpMathPoint.x), Points.push_back(mpMathPoint.y);
                    else
                        Points.push_back(left2X), Points.push_back(left2Y),
                        Points.push_back(mpMathPoint.x), Points.push_back(mpMathPoint.y),
                        Points.push_back(mpMathPoint.x), Points.push_back(mpMathPoint.y),   // doubling inner point!
                        Points.push_back(right2X), Points.push_back(right2Y),
                        Weights.push_back(1.0), Weights.push_back(1.0);   // doubling inner point!
                    Weights.push_back(1.0);
                    Weights.push_back(1.0);
                }
                if (closed_path) {
                    pBezier->GetPoint(0,   &base2X, &base2Y, &left2X, &left2Y, &right2X, &right2Y);
                    //real_count++;
                    Points.push_back(left2X), Points.push_back(left2Y),
                    Points.push_back(base2X), Points.push_back(base2Y);
                    Weights.push_back(1.0);
                    Weights.push_back(1.0);
                }
            } else {
//Message("Bezier swapped");
                for (int basepoint = basepoint_count - 1; basepoint > -step; basepoint -= step)
                {
                    if (basepoint < 0) basepoint = 0;

                    GetArrayItem(bpBezierParam.pMathPoint, basepoint, &mpMathPoint, sizeof(MathPointParam));

                    pBezier->GetPoint(basepoint,   &base2X, &base2Y, &left2X, &left2Y, &right2X, &right2Y);
/*
                    LONG i = 6 * basepoint + 0, j = 6 * basepoint + 1;      // B,L,R -> L,R,B !
                    ::SafeArrayGetElement( pSrc, &i, &left2X);
                    ::SafeArrayGetElement( pSrc, &j, &left2Y);
                    i += 2, j += 2;
                    ::SafeArrayGetElement( pSrc, &i, &base2X);
                    ::SafeArrayGetElement( pSrc, &j, &base2Y);
                    i += 2, j += 2;
                    ::SafeArrayGetElement( pSrc, &i, &right2X);
                    ::SafeArrayGetElement( pSrc, &j, &right2Y);
*/
                    ASSERT(mpMathPoint.x == base2X && mpMathPoint.y == base2Y)

                    //real_count++;
                    if (basepoint == 0)
                        Points.push_back(right2X), Points.push_back(right2Y),
                        Points.push_back(mpMathPoint.x), Points.push_back(mpMathPoint.y);
                    else if (basepoint == basepoint_count - 1)
                        Points.push_back(mpMathPoint.x), Points.push_back(mpMathPoint.y),
                        Points.push_back(left2X), Points.push_back(left2Y);
                    else
                        Points.push_back(right2X), Points.push_back(right2Y),
                        Points.push_back(mpMathPoint.x), Points.push_back(mpMathPoint.y),
                        Points.push_back(mpMathPoint.x), Points.push_back(mpMathPoint.y),   // doubling inner point!
                        Points.push_back(left2X), Points.push_back(left2Y),
                        Weights.push_back(1.0), Weights.push_back(1.0);   // doubling inner point!
                    Weights.push_back(1.0);
                    Weights.push_back(1.0);
                }
            }
            real_count = Weights.size();    // = Points.size() / 2;

            SAFEARRAYBOUND sabNewArray;
            sabNewArray.cElements = real_count * 2;
            sabNewArray.lLbound = 0;
            SAFEARRAY * pSafe = ::SafeArrayCreate( VT_R8, 1, &sabNewArray );
            for (LONG i = 0; i < (LONG)sabNewArray.cElements; i += 2)
            {
                LONG i0 = i;
                ::SafeArrayPutElement( pSafe, &i0, &Points[i + 0]); i0++;
                ::SafeArrayPutElement( pSafe, &i0, &Points[i + 1]);
            }
            VARIANT varRow;
            VariantInit(&varRow);
            V_VT(&varRow) = VT_ARRAY | VT_R8;
            V_ARRAY(&varRow) = pSafe;

            SAFEARRAYBOUND sabNewArray1;
            sabNewArray1.cElements = real_count;
            sabNewArray1.lLbound = 0;
            SAFEARRAY * pSafe1 = ::SafeArrayCreate( VT_R8, 1, &sabNewArray1 );
            for (LONG i = 0; i < (LONG)sabNewArray1.cElements;)
            {
                ::SafeArrayPutElement( pSafe1, &i, &Weights[ i ]); i++;
            }
            VARIANT varRow1;
            VariantInit(&varRow1);
            V_VT(&varRow1) = VT_ARRAY | VT_R8;
            V_ARRAY(&varRow1) = pSafe1;

            VARIANT varRow2;
            VariantInit(&varRow2);
            SAFEARRAYBOUND sabNewArray2;
            SAFEARRAY * pSafe2 = 0;
//Message("test0");
            if (Knots.size() == 0 && real_count >= degree) {    // always
                sabNewArray2.cElements = real_count + degree;
                sabNewArray2.lLbound = 0;
                pSafe2 = ::SafeArrayCreate( VT_R8, 1, &sabNewArray2 );
                for (LONG i = 0; i < (LONG)sabNewArray2.cElements; i++)
                {
//                    double data = i < degree? 0.0 : i >= real_count ? 1.0 : double(i - degree + 1) / (real_count - degree + 1) ;
                    double data = (i / 4);      // 0, 1, ... , max = [(real_count + degree) / 4 - 1]
                    ::SafeArrayPutElement( pSafe2, &i, &data);
                    Knots.push_back(data);
                }
            }
            V_VT(&varRow2) = VT_ARRAY | VT_R8;
            V_ARRAY(&varRow2) = pSafe2;

            //ICurve2DPtr temp_curve = mathp->Nurbs(closed_path, degree, varRow, varRow1, varRow2);
            ICurve2DPtr temp_curve;
            //? Closed bezier gives fault!
            temp_curve = mathp->Nurbs((closed_path? FALSE : FALSE), degree, varRow, varRow1, varRow2);

/*            VARIANT uvarRow;
            VariantInit(&uvarRow);
            VARIANT uvarRow1;
            VariantInit(&uvarRow1);
            VARIANT uvarRow2;
            VariantInit(&uvarRow2);
            double tmin, tmax;
            temp_curve->GetNurbsParams(FALSE, &uvarRow, &uvarRow1, &uvarRow2, &tmin, &tmax);
*/
            reference rr = temp_curve->Reference; //0!
            //temp_curve->Release(); BAD!

            double delta = CurvesDeviation(element, temp_curve, m_precision * m_MmToUnits, &old_Points[0], basepoint_count);
            if (delta <= m_precision * m_MmToUnits || step == 1) break;
//Message("test");
            split_number = std::max(split_number * 2, degree);
            {
                real_count = 0;
                Points.clear();
                Weights.clear();
                Knots.clear();
            }
        } while (step > 1);

        reference new_curve = 0;

        do {
            NurbsPointParam par;
            //Nurbs(degree, closed_path, 1);
            Nurbs(degree, false, 1);            //? Closed bezier gives error in Equidistant!

//swapped = true;
            if (swapped == false || closed_path) {
                for (int i = 0; i < real_count; i++)
                {
                    par.x = Points[2 * i];
                    par.y = Points[2 * i + 1];
                    par.weight = Weights[i];
                    NurbsPoint(&par);
                }
                if (step == 1 && Knots.size() > 0)
                    for (int knot = 0; knot < (int)Knots.size(); knot ++)
                    {
                        ksNurbsKnot(Knots[knot]);
                    }
            }
            else {
//Message("NURBS swapped");
                for (int i = 0; i < real_count; i++)
                {
                    par.x = Points[2 * i];
                    par.y = Points[2 * i + 1];
                    par.weight = Weights[i];
                    NurbsPoint(&par);
                }
                if (step == 1 && Knots.size() > 0) {
                    double knotmax = Knots.back();
                    for (int knot = Knots.size() - 1; knot >= 0; knot --)
                    {
                        double knotdata = Knots[knot];
                        int res = ksNurbsKnot(knotmax - knotdata);
                        if (!res) Message("BAD knot");
                    }
                }
            }
            new_curve = EndObj();

#if defined(DO_DEBUG)
//if (new_curve == 0) Message("DrawBezierWithNurbs returns ref == 0!");
#endif
            if (new_curve) return new_curve;    // => new_curve may be 0! like for Bezier !?
        } while (0);
    }
    return 0;
}

reference CSewingDlg::DrawBezier(reference element, bool closed)        // new API7 version
{
//Message("DrawBezier");
    IBezierPtr pBezier = ksTransferReference(element, 0);
    if (!pBezier) return 0;

    BezierParam bpBezierParam;
    int step = 1;
    bool swapped = false;
    if (GetObjParam(element, &bpBezierParam, sizeof(BezierParam), ALLPARAM))
    {
        const int basepoint_count = GetArrayCount(bpBezierParam.pMathPoint);
        if (bpBezierParam.pMathPoint == 0 || basepoint_count == 0) return 0;

        //ASSERT(AllPointsCount == basepoint_count)

        MathPointParam mpMathPoint;
        GetArrayItem(bpBezierParam.pMathPoint, 0, &mpMathPoint, sizeof(MathPointParam));
        double x = mpMathPoint.x,
               y = mpMathPoint.y;
        double xn, yn;
        bool swapped = false;

        if (!closed) {
            GetArrayItem(bpBezierParam.pMathPoint, basepoint_count - 1, &mpMathPoint, sizeof(MathPointParam));
            xn = mpMathPoint.x, yn = mpMathPoint.y;
            swapped = CheckSwapBegPoint(x, y, xn, yn);
        }

        const int split_number = std::max(m_MaxSplitNum, 4);
        if (basepoint_count > split_number) step = basepoint_count / split_number;
    }

    const BOOL Bezier_mode = (step > 1)? FALSE : TRUE;

    ULONG AllPointsCount = pBezier->PointsCount,
         OutPointsCount = (step == 1)? AllPointsCount :
         int((AllPointsCount + step - 1) / step) + ((AllPointsCount % step == 1)? 0 : 1);
    //variant_t AllPoints = pBezier->GetPoints(FALSE);
    //AllPoints = pBezier->GetPoints(AllPoints);
    VARIANT varRow;
    VariantInit(&varRow);
    varRow = pBezier->GetPoints(Bezier_mode);
    V_VT(&varRow) = VT_ARRAY | VT_R8;

   // IMath2DPtr MathDrawer = pBezier->Application->Math2D;
   // ICurve2DPtr MathCurve = MathDrawer->Bezier(closed, Bezier_mode, varRow);

    ksDocument2DPtr m_pKompasDoc5 = kompasAPI->ActiveDocument2D();  // Current Kompas document       (for KAPI5 usage)
    IKompasDocumentPtr doc7 = newKompasAPI->ActiveDocument; // ksTransferReference( m_pKompasDoc5->reference, 0 );
    IKompasDocument2DPtr pkdDocument = 0;
    doc7->QueryInterface(&pkdDocument);

    IViewPtr ivpView = pkdDocument->ViewsAndLayersManager->Views->View[0];
    IDrawingContainerPtr pdcContainer = 0;
    ivpView->QueryInterface(&pdcContainer);

    IDrawingObjectPtr pdoDrawingObject = 0;
    ivpView->QueryInterface(&pdoDrawingObject);

    IBeziersPtr Bezier_pool = pdcContainer->Beziers;
    IBezierPtr Bezier_curve = Bezier_pool->Add();

    Bezier_curve->Closed = closed;
    Bezier_curve->Style = 1;

    SAFEARRAYBOUND sabNewArray;
    sabNewArray.cElements = OutPointsCount * (Bezier_mode? 6 : 2);
    sabNewArray.lLbound = 0;

    SAFEARRAY * pSafe = ::SafeArrayCreate( VT_R8, 1, &sabNewArray );

//if (step > 1)
//Message("step > 1");

//swapped = true;
    if (swapped == false || closed)//_path)
    {
//Message("swapped == false");
        SAFEARRAY * pSrc = V_ARRAY(&varRow);
        double arrdata;
        if (Bezier_mode == FALSE)
            for (ULONG i0 = 0, j0 = 0; j0 < sabNewArray.cElements; i0 += 2 * step, j0 += 2)
            {
                if (i0 >= 2 * AllPointsCount) i0 = 2 * (AllPointsCount - 1);
                LONG i = i0, j = j0;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
                i++, j++;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
            }
        else
        {
            for (ULONG i0 = 0, j0 = 0; j0 < sabNewArray.cElements; i0 += 6 * step, j0 += 6)      // B,L,R -> L,B,R !
            {
                if (i0 >= 6 * AllPointsCount) i0 = 6 * (AllPointsCount - 1);
                LONG i = i0, j = j0 + 2;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
                i++, j++;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
                i = i0 + 2, j = j0;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
                i++, j++;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
                i = i0 + 4, j = j0 + 4;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
                i++, j++;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
            }
        }

        VARIANT varRow0;
        VariantInit(&varRow0);
        V_VT(&varRow0) = VT_ARRAY | VT_R8;
        V_ARRAY(&varRow0) = pSafe;

        long OldPointsCount = Bezier_curve->PointsCount;
        Bezier_curve->PutPoints(Bezier_mode, varRow0);
        long NewPointsCount = Bezier_curve->PointsCount;
        BOOL istemp = Bezier_curve->Temp;
        BOOL isok = Bezier_curve->Update();
    }
    else
    {
//Message("Swapped");
        SAFEARRAY * pSrc = V_ARRAY(&varRow);
        double arrdata;
        if (Bezier_mode == FALSE)
            for (ULONG i0 = 0, j0 = 0; j0 < sabNewArray.cElements; i0 += 2 * step, j0 += 2)
            {
                if (i0 >= 2 * AllPointsCount) i0 = 2 * (AllPointsCount - 1);
                LONG i = i0, j = sabNewArray.cElements - j0 - 2;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
                i++, j++;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
            }
        else
        {
            for (ULONG i0 = 0, j0 = 0; j0 < sabNewArray.cElements; i0 += 6 * step, j0 += 6)      // B,L,R -> L,R,B !
            {
                if (i0 >= 6 * AllPointsCount) i0 = 6 * (AllPointsCount - 1);
                LONG i = i0, j = sabNewArray.cElements - j0 - 2;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
                i++, j++;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
                i = i0 + 2, j = sabNewArray.cElements - j0 - 6;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
                i++, j++;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
                i = i0 + 4, j = sabNewArray.cElements - j0 - 4;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
                i++, j++;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
            }
        }

        VARIANT varRow0;
        VariantInit(&varRow0);
        V_VT(&varRow0) = VT_ARRAY | VT_R8;
        V_ARRAY(&varRow0) = pSafe;

        long OldPointsCount = Bezier_curve->PointsCount;
        Bezier_curve->PutPoints(Bezier_mode, varRow0);
        long NewPointsCount = Bezier_curve->PointsCount;
        BOOL istemp = Bezier_curve->Temp;
        BOOL isok = Bezier_curve->Update();
    }
/*
    IUnknownPtr pUnk0 = ksTransferReference( element, 0 );

    IDrawingObjectPtr doDrawingObject = NULL;
    pUnk0->QueryInterface(&doDrawingObject);

    doDrawingObject->LayerNumber = doDrawingObject->LayerNumber;
    if (!doDrawingObject->Update()) Message("NO update object");

    IDrawingObjectPtr doDrawingObject1 = NULL;
    Bezier_curve->QueryInterface(&doDrawingObject1);

    doDrawingObject1->LayerNumber = doDrawingObject1->LayerNumber;
    if (!doDrawingObject1->Update()) Message("NO update object1");

    if (!pdoDrawingObject->Update()) Message("NO update view");*/
    reference ref = Bezier_curve->Reference;

    PumpWaitingMessages();

    //pdoDrawingObject->LayerNumber = pdoDrawingObject->LayerNumber;
    //if (!pdoDrawingObject->Update()) Message("NO update object");
    //PumpWaitingMessages();

    /*    VARIANT varRow1;
    VariantInit(&varRow1);
    varRow1 = pBezier->GetPoints(FALSE);
    V_VT(&varRow1) = VT_ARRAY | VT_R8;

    std::vector<double> old_points(AllPointsCount * 2);

    SAFEARRAY * pSrc1 = V_ARRAY(&varRow1);
    for (LONG i = 0; i < (LONG)(2 * AllPointsCount); i++)
    {
        ::SafeArrayGetElement( pSrc1, &i, &old_points[i]);
    }

    double delta = CurvesDeviation(IDrawingObjectPtr(pBezier), IDrawingObjectPtr(Bezier_curve), &old_points[0], AllPointsCount);
*/
#if defined(DO_DEBUG)
//if (ref == 0) Message("DrawBezier returns ref == 0!");
#endif
    return ref;         //? if (!ref) Message("ref == 0"); => ref may be 0!
}

reference CSewingDlg::DrawRectangle(reference element)
{
    RectangleParam rpRectangleParam;
    if (GetObjParam(element, &rpRectangleParam, sizeof(RectangleParam), ALLPARAM))
    {
        double x = rpRectangleParam.x,
               y = rpRectangleParam.y;
        double angle = rpRectangleParam.ang;
        double w = rpRectangleParam.width,
               h = rpRectangleParam.height;

        rpRectangleParam.style = 1;

        double perimeter =  ksGetCurvePerimeter (element, ST_MIX_MM);

        //rpRectangleParam.pCorner = ::CreateArray(CORNER_ARR, 0); // Создание массива параметров углов 

        return ksRectangle(&rpRectangleParam, 0);
    }
    return 0;
}

reference CSewingDlg::DrawRegularPolygon(reference element)
{
    RegularPolygonParam rppRegularPolygonParam;
    if (GetObjParam(element, &rppRegularPolygonParam, sizeof(RegularPolygonParam), ALLPARAM))
    {
        double xc = rppRegularPolygonParam.xc,
               yc = rppRegularPolygonParam.yc;
        double angle = rppRegularPolygonParam.ang;
        double r = rppRegularPolygonParam.radius;
        int sides = rppRegularPolygonParam.count;
        int inscribed = rppRegularPolygonParam.describe;

        rppRegularPolygonParam.style = 1;

        double perimeter =  ksGetCurvePerimeter (element, ST_MIX_MM);

        //rppRegularPolygonParam.pCorner = ::CreateArray(CORNER_ARR, 0); // Создание массива параметров углов 

        return ksRegularPolygon(&rppRegularPolygonParam, 0);
    }
    return 0;
}

reference CSewingDlg::DrawPolyline(reference element, bool closed)
{
    PolylineParamEx ppPolylineParam;

    if (GetObjParam(element, &ppPolylineParam, sizeof(PolylineParamEx), ALLPARAM))
    {
        const int vertex_count = GetArrayCount(ppPolylineParam.pMathPoint);
        if (ppPolylineParam.pMathPoint == 0 || vertex_count == 0) return 0;

        MathPointParam mpMathPoint;
        GetArrayItem(ppPolylineParam.pMathPoint, 0, &mpMathPoint, sizeof(MathPointParam));
        double x = mpMathPoint.x,
               y = mpMathPoint.y;
        double xn, yn;
        bool swapped = false;

        const double perimeter =  ksGetCurvePerimeter (element, ST_MIX_MM);
        const bool closed_path = (ppPolylineParam.closed != 0);

        if (!closed_path) {
            GetArrayItem(ppPolylineParam.pMathPoint, vertex_count - 1, &mpMathPoint, sizeof(MathPointParam));
            xn = mpMathPoint.x, yn = mpMathPoint.y;
            swapped = CheckSwapBegPoint(x, y, xn, yn);
        }

        ksPolyline(1);

        if (swapped == false || closed_path)
            for (int vertex = 0; vertex < vertex_count; vertex++)
            {
                GetArrayItem(ppPolylineParam.pMathPoint, vertex, &mpMathPoint, sizeof(MathPointParam));
                Point(mpMathPoint.x, mpMathPoint.y, 0);
            }
        else
            for (int vertex = vertex_count - 1; vertex >= 0; vertex--)
            {
                GetArrayItem(ppPolylineParam.pMathPoint, vertex, &mpMathPoint, sizeof(MathPointParam));
                Point(mpMathPoint.x, mpMathPoint.y, 0);
            }
        return EndObj();
    }
    return 0;
}

reference CSewingDlg::DrawContour(reference element, bool closed)
{
    return 0;
    return DrawApproximation(element, CONTOUR_OBJ);
    //DrawProtoContour(element, DeferDrawing, parentDrawingInView);
}

reference CSewingDlg::DrawNurbs(reference element, bool closed)     // new API7+IMath2D
{
//Message("DrawNURBS");
    INurbsPtr pNurbs = ksTransferReference(element, 0);
    if (!pNurbs) return 0;

    NurbsParam npNurbsParam;
    if (GetObjParam(element, &npNurbsParam, sizeof(NurbsParam), ALLPARAM))
    {
        std::vector<double> old_Points;

        const int basepoint_count = GetArrayCount(npNurbsParam.pPoint);
        const int knot_count = GetArrayCount(npNurbsParam.pKnot);
        if (npNurbsParam.pPoint == 0 || basepoint_count == 0) return 0;

        int degree = npNurbsParam.degree;

        NurbsPointParam npPoint;

        for (int i = 0; i < basepoint_count; i++) {
            GetArrayItem(npNurbsParam.pPoint, i, &npPoint, sizeof(npPoint));
            old_Points.push_back(npPoint.x);
            old_Points.push_back(npPoint.y);
        }

        GetArrayItem(npNurbsParam.pPoint, 0, &npPoint, sizeof(npPoint));
        double x = npPoint.x,
               y = npPoint.y,
               w = npPoint.weight;
        double xn, yn;
        bool swapped = false;

        const double perimeter =  ksGetCurvePerimeter (element, ST_MIX_MM);
        const bool closed_path = (npNurbsParam.close != 0 && closed);

        bool knotted  = (npNurbsParam.pKnot != 0);

        if (!closed_path) {
            GetArrayItem(npNurbsParam.pPoint, basepoint_count - 1, &npPoint, sizeof(npPoint));
            xn = npPoint.x, yn = npPoint.y;
            swapped = CheckSwapBegPoint(x, y, xn, yn);
        }

        int step = 1;
        int split_number = std::max(m_MaxSplitNum, degree);

        IMath2DPtr mathp = newKompasAPI->Application->Math2D;

        do {
            if (basepoint_count > split_number) step = basepoint_count / split_number;

            //double base1X, base1Y, left1X, left1Y, right1X, right1Y;
            double base2X, base2Y, Weight;

            NurbsPointParam par;
            //Nurbs(degree, closed, 1);

            int real_count = 0;
            std::vector<double> Points;
            std::vector<double> Weights;
            std::vector<double> Knots;

//swapped = true;
            if (swapped == false || closed_path) {
                for (int basepoint = 0; basepoint < basepoint_count + step-1; basepoint += step)
                {
                    if (basepoint >= basepoint_count) basepoint = basepoint_count - 1;

                    GetArrayItem(npNurbsParam.pPoint, basepoint, &npPoint, sizeof(npPoint));

                    pNurbs->GetPoint(basepoint,   &base2X, &base2Y, &Weight);

                    ASSERT(npPoint.x == base2X && npPoint.y == base2Y && npPoint.weight == Weight)
                    par.x = base2X;
                    par.y = base2Y;
                    par.weight = Weight;

                    real_count++;
                    Points.push_back(par.x), Points.push_back(par.y);
                    Weights.push_back(Weight);
                }
                if (step == 1 && knotted && knot_count > 0)
                    for (int knot = 0; knot < knot_count + step-1; knot += step)
                    {
                        double knotdata;
                        GetArrayItem(npNurbsParam.pKnot, knot, &knotdata, sizeof(knotdata));
                        Knots.push_back(knotdata);
                    }
            }
            else {
//Message("NURBS swapped");
                for (int basepoint = basepoint_count - 1; basepoint > -step; basepoint -= step)
                {
                    if (basepoint < 0) basepoint = 0;

                    GetArrayItem(npNurbsParam.pPoint, basepoint, &npPoint, sizeof(npPoint));

                    pNurbs->GetPoint(basepoint,   &base2X, &base2Y, &Weight);

                    ASSERT(npPoint.x == base2X && npPoint.y == base2Y && npPoint.weight == Weight)
                    par.x = base2X;
                    par.y = base2Y;
                    par.weight = Weight;

                    real_count++;
                    Points.push_back(par.x), Points.push_back(par.y);
                    Weights.push_back(Weight);
                }
                if (step == 1 && knotted && knot_count > 0) {
                    double knotmax;
                    GetArrayItem(npNurbsParam.pKnot, knot_count - 1, &knotmax, sizeof(knotmax));
                    for (int knot = knot_count - 1; knot > -step; knot -= step)
                    {
                        double knotdata;
                        GetArrayItem(npNurbsParam.pKnot, knot, &knotdata, sizeof(knotdata));
                        Knots.push_back(knotmax - knotdata);
                    }
                }
            }

            SAFEARRAYBOUND sabNewArray;
            sabNewArray.cElements = real_count * 2;
            sabNewArray.lLbound = 0;
            SAFEARRAY * pSafe = ::SafeArrayCreate( VT_R8, 1, &sabNewArray );
            for (LONG i = 0; i < (LONG)sabNewArray.cElements; i += 2)
            {
                LONG i0 = i;
                ::SafeArrayPutElement( pSafe, &i0, &Points[i + 0]); i0++;
                ::SafeArrayPutElement( pSafe, &i0, &Points[i + 1]);
            }
            VARIANT varRow;
            VariantInit(&varRow);
            V_VT(&varRow) = VT_ARRAY | VT_R8;
            V_ARRAY(&varRow) = pSafe;

            SAFEARRAYBOUND sabNewArray1;
            sabNewArray1.cElements = real_count;
            sabNewArray1.lLbound = 0;
            SAFEARRAY * pSafe1 = ::SafeArrayCreate( VT_R8, 1, &sabNewArray1 );
            for (LONG i = 0; i < (LONG)sabNewArray1.cElements;)
            {
                ::SafeArrayPutElement( pSafe1, &i, &Weights[ i ]); i++;
            }
            VARIANT varRow1;
            VariantInit(&varRow1);
            V_VT(&varRow1) = VT_ARRAY | VT_R8;
            V_ARRAY(&varRow1) = pSafe1;

            VARIANT varRow2;
            VariantInit(&varRow2);
            SAFEARRAYBOUND sabNewArray2;
            SAFEARRAY * pSafe2 = 0;
//Message("test0");
            if (Knots.size() > 0) {
                sabNewArray2.cElements = Knots.size();
                sabNewArray2.lLbound = 0;
                pSafe2 = ::SafeArrayCreate( VT_R8, 1, &sabNewArray2 );
                for (LONG i = 0; i < (LONG)sabNewArray2.cElements;)
                {
                    ::SafeArrayPutElement( pSafe2, &i, &Knots[ i ]); i++;
                }
            } else {
                sabNewArray2.cElements = real_count + degree;
                sabNewArray2.lLbound = 0;
                pSafe2 = ::SafeArrayCreate( VT_R8, 1, &sabNewArray2 );
                for (LONG i = 0; i < (LONG)sabNewArray2.cElements; i++)
                {
                    double data = i < degree? 0.0 : i > real_count ? 1.0 : double(i) / real_count ;
                    ::SafeArrayPutElement( pSafe2, &i, &data);
                }
            }
            V_VT(&varRow2) = VT_ARRAY | VT_R8;
            V_ARRAY(&varRow2) = pSafe2;

            //ICurve2DPtr temp_curve = mathp->Nurbs(closed_path, degree, varRow, varRow1, varRow2);
            ICurve2DPtr temp_curve;
            temp_curve = mathp->Nurbs(closed_path, degree, varRow, varRow1, varRow2);

/*            VARIANT uvarRow;
            VariantInit(&uvarRow);
            VARIANT uvarRow1;
            VariantInit(&uvarRow1);
            VARIANT uvarRow2;
            VariantInit(&uvarRow2);
            double tmin, tmax;
            temp_curve->GetNurbsParams(FALSE, &uvarRow, &uvarRow1, &uvarRow2, &tmin, &tmax);
*/
            reference rr = temp_curve->Reference; //0!
            //temp_curve->Release(); BAD!

            double delta = CurvesDeviation(element, temp_curve, m_precision * m_MmToUnits, &old_Points[0], basepoint_count);
            if (delta <= m_precision * m_MmToUnits) break;
//Message("test");
            split_number = std::max(split_number * 2, degree);
        } while (step > 1);

        reference new_curve = 0;

        do {
            //double base1X, base1Y, left1X, left1Y, right1X, right1Y;
            double base2X, base2Y, Weight;

            NurbsPointParam par;
            Nurbs(degree, closed, 1);

            int real_count = 0;

//swapped = true;
if (swapped == false || closed_path) {
                for (int basepoint = 0; basepoint < basepoint_count + step-1; basepoint += step)
                {
                    if (basepoint >= basepoint_count) basepoint = basepoint_count - 1;

                    GetArrayItem(npNurbsParam.pPoint, basepoint, &npPoint, sizeof(npPoint));

                    pNurbs->GetPoint(basepoint,   &base2X, &base2Y, &Weight);

                    ASSERT(npPoint.x == base2X && npPoint.y == base2Y && npPoint.weight == Weight)
                    par.x = base2X;
                    par.y = base2Y;
                    par.weight = Weight;
                    NurbsPoint(&par);

                    real_count++;
                }
                if (step == 1 && knotted && knot_count > 0)
                    for (int knot = 0; knot < knot_count + step-1; knot += step)
                    {
                        double knotdata;
                        GetArrayItem(npNurbsParam.pKnot, knot, &knotdata, sizeof(knotdata));
                        ksNurbsKnot(knotdata);
                    }
            }
            else {
//Message("NURBS swapped");
                for (int basepoint = basepoint_count - 1; basepoint > -step; basepoint -= step)
                {
                    if (basepoint < 0) basepoint = 0;

                    GetArrayItem(npNurbsParam.pPoint, basepoint, &npPoint, sizeof(npPoint));

                    pNurbs->GetPoint(basepoint,   &base2X, &base2Y, &Weight);

                    ASSERT(npPoint.x == base2X && npPoint.y == base2Y && npPoint.weight == Weight)
                    par.x = base2X;
                    par.y = base2Y;
                    par.weight = Weight;
                    NurbsPoint(&par);

                    real_count++;
                }
                if (step == 1 && knotted && knot_count > 0) {
                    double knotmax;
                    GetArrayItem(npNurbsParam.pKnot, knot_count - 1, &knotmax, sizeof(knotmax));
                    for (int knot = knot_count - 1; knot > -step; knot -= step)
                    {
                        double knotdata;
                        GetArrayItem(npNurbsParam.pKnot, knot, &knotdata, sizeof(knotdata));
                        int res = ksNurbsKnot(knotmax - knotdata);
                        if (!res) Message("BAD knot");
                    }
                }
            }
            new_curve = EndObj();
#if defined(DO_DEBUG)
//if (new_curve == 0) Message("DrawNurbs returns ref == 0!");
#endif
            if (new_curve) return new_curve;    // => new_curve may be 0! like for Bezier !?
        } while (0);
    }
    return 0;

    //return DrawApproximation(element, NURBS_OBJ);
    //DrawNurbsAsContour(element);
}

double CSewingDlg::CurvesDeviation(reference old_curve, ICurve2DPtr& new_curve, double precision, double* tpars, int points_count)
{
//    return 0;
//Message("CurvesDeviation!");
    double delta = 0.0, average = 0.0;
    double x, y, kx, ky, t, angle, data;
    int count = 0;
    for (int i = 0; i < points_count; i++) {
        x = tpars[2 * i];
        y = tpars[2 * i + 1];
        ::ksGetCurvePointProjection (old_curve, x, y, &x, &y);
        //if (new_curve->PointProjection(x, y, &kx, &ky, &t, &angle))   BAD KOPAS - returns FALSE when success
        new_curve->PointProjection(x, y, &kx, &ky, &t, &angle);
            data = DistancePntPnt(x, y, kx, ky),
            delta = std::max(delta, data),
            average += data, count++;
        if (delta > precision) return delta;    // to speed up this loop
    }
    if (count > 0)average /= count;
    return delta;
}

reference CSewingDlg::DrawApproximation(reference element, int curve_type)
{
Message("DrawApproximation");
    if (curve_type != CONTOUR_OBJ) return 0;
    return 0;
}

// structs declared on local level to debug in VC-debugger (MS bug)
struct Edge {
    double x1; double y1;
    double x2; double y2;
    reference curve;
    size_t node_index1;     // aux field denotes Index of vertex 1 (ordered from 1; 0 means UNDEFINED)
    size_t node_index2;     // aux field denotes Index of vertex 2 (ordered from 1; 0 means UNDEFINED)
    size_t link_index;      // aux vertex/edge component index, used later as work field (1-ordered; 0 means UNDEFINED)
    size_t back_index1, back_index2, back_count;       // back indexes for special case
    Edge(double ux1, double uy1, double ux2, double uy2, reference ucurve)
        : x1(ux1), y1(uy1), x2(ux2), y2(uy2), curve(ucurve), link_index(0), node_index1(0), node_index2(0)
          ,back_count(0), back_index1(0), back_index2(0) {}
};
struct CurveType {
    unsigned short style;
    unsigned long color;
    double width;
    std::vector<Edge> Curves;
    CurveType(unsigned short ustyle, unsigned long ucolor, double uwidth)
        : style(ustyle), color(ucolor), width(uwidth) {}
};
struct LineEnd {
    double x; double y;
    size_t index;   // index into Curves (ordered from 1; 0 means UNDEFINED)
    size_t single_loop;             // special case
    LineEnd(double ux, double uy, size_t uindex) : x(ux), y(uy), index(uindex), single_loop(0) {}
    //static bool SortedX (const LineEnd & lthis, const LineEnd & lend) { return lthis.x < lend.x; }
    static bool SortedYpostX (const LineEnd & lthis, const LineEnd & lend) { return equal_values(lthis.x, lend.x)? (lthis.y < lend.y) : (lthis.x < lend.x); }
};
struct Node {
    double x; double y;
    size_t edge_index;      // (ordered from 1; 0 means UNDEFINED)
    size_t node_index;      // aux field denotes Index of vertex (ordered from 1; 0 means UNDEFINED)
    size_t link_index;      // aux field used later to detect components (ordered from 1; 0 means UNDEFINED)
    Node(double ux, double uy, size_t uindex) : x(ux), y(uy), edge_index(uindex), node_index(0), link_index(0) {}
    Node(double ux, double uy, size_t uindex, size_t uNindex, size_t uLindex) : x(ux), y(uy), edge_index(uindex), node_index(uNindex), link_index(uLindex) {}
};
struct NodeLinks {
    Node thisNode;
    size_t edge_count;      // aux count
    size_t parent_index;    // aux index (ordered from 1; 0 means UNDEFINED)
    size_t inner_edge_index;  // aux index (ordered from 1; 0 means UNDEFINED)
    std::vector<Node> Nodes;
    NodeLinks(Node& uNode) : thisNode(uNode.x, uNode.y, uNode.edge_index, uNode.node_index, uNode.link_index),
                             edge_count(0), parent_index(0), inner_edge_index(0) {}
};

struct equi_contour {
    reference contour;
    reference parent;
    bool issingle;
    bool swapped_curve;
    std::vector <reference> equi_curves;
    int equi_curves_count;      // may differ from equi_curves.size() due to m_grpAuxScratches
    int enclose_count;      // testing or SLOW VERSION
    static CSewingDlg* pThis;
    equi_contour(reference r, bool single, bool swapped, std::vector <reference>& curves, int curves_count)
        : contour(r), issingle(single), swapped_curve(swapped), parent(0), enclose_count(0), equi_curves_count(curves_count)
    {
        if (curves.size() > 0) equi_curves.insert(equi_curves.begin(), curves.begin(), curves.end());
    }
    static bool SortedContours (const equi_contour & lthis, const equi_contour & lend) {
        if (lthis.parent && !lend.parent) return true;
        //if (lthis.parent && lend.parent) return IsContourInsideContour(lthis.parent, lend.parent, pThis);
        return IsContourInsideContour(lthis.contour, lend.contour, pThis); }
};
CSewingDlg* equi_contour::pThis = 0;

void CSewingDlg::PrepareContoursMakeEquidistants(reference obj_iterator, EquidistantParam *equiparam)
{
    std::vector<CurveType> locCurvesPool;

    //ATTENTION - decomposed_reference group & iterator MUST be kept int tmp group HERE for docomposed objects be valid!
//    reference rGroup = NewGroup(1);
//    EndGroup();

    do {    // cycle to collect from view objects (no need if part_reference is some else but view)

        reference cur_object_reference = MoveIterator(obj_iterator, 'F');
        ksCurveStyleParamPtr styleParam = kompasAPI->GetParamStruct(ko_CurveStyleParam);
        while (cur_object_reference)
        {
            int type = GetObjParam(cur_object_reference, 0, 0, ALLPARAM);
            int obj_style = ksGetObjectStyle(cur_object_reference);
            //bool accepted = (type == LINESEG_OBJ || type == ARC_OBJ) && obj_style != CURVE_AXIS3_SPECIAL;       // decomposition produces only these types!
            bool accepted = (type != 0 && type != EQUID_OBJ && (type != LINESEG_OBJ || obj_style != EQUI_STYLE));
            bool approxed = (type == CONTOUR_OBJ);//?? || type == BEZIER_OBJ); // || type == NURBS_OBJ);

            do {    // pseudocycle to handle special breakes
                if (!ExistObj( cur_object_reference ) || !IsGeomObject( cur_object_reference )) break;
                if (accepted == false || approxed) break;

                ksDocument2DPtr m_pKompasDoc5 = kompasAPI->ActiveDocument2D();  // Current Kompas document       (for KAPI5 usage)
                if (m_pKompasDoc5->ksGetStyleParam(CURVE_STYLE_EX, (short)obj_style, styleParam) == 0) break;

                double t1, t2, x1, y1, x2, y2;
                ksGetCurveMinMaxParametr (cur_object_reference, &t1, &t2);
                ksGetCurvePoint(cur_object_reference, t1, &x1, &y1);
                ksGetCurvePoint(cur_object_reference, t2, &x2, &y2);
                //if ((m_SaveViewMatricesPdfMode > 0 || m_EMULATION_state == false))      //? && m_CurrentViewIndex != 0)
                {
                    ksPointIntoMtr (x1, y1, &x1, &y1);
                    ksPointIntoMtr (x2, y2, &x2, &y2);
                }

                if (m_ContourHasAnyLineStyles != 0) {
                    if (locCurvesPool.size() == 0) locCurvesPool.push_back(CurveType(0, 0, 0));
                    locCurvesPool[0].Curves.push_back(Edge(x1, y1, x2, y2, cur_object_reference));
                    continue;
                }

                bool found = false;
                for (size_t i = 0; i < locCurvesPool.size(); i++)
                {
                    if (obj_style == locCurvesPool[i].style && styleParam->color == locCurvesPool[i].color && styleParam->paperWidth == locCurvesPool[i].width)
                    {
                        locCurvesPool[i].Curves.push_back(Edge(x1, y1, x2, y2, cur_object_reference));
                        found = true; break;
                    }
                }
                if (!found) {
                    locCurvesPool.push_back(CurveType(obj_style, styleParam->color, styleParam->paperWidth));
                    locCurvesPool[locCurvesPool.size()-1].Curves.push_back(Edge(x1, y1, x2, y2, cur_object_reference));
                }
            } while (0);

            if (!IsGeomObject( cur_object_reference ) || accepted == false)
            {
                //SKIP!     //Draw2DElement(cur_object_reference);        // drawing texts
            }

            cur_object_reference = MoveIterator(obj_iterator, 'N');
        }

    } while (0);    //while (needIterator && part_reference != 0);

    CWnd * pCWnd = NULL;
    pCWnd = CWnd::FromHandlePermanent( (HWND) GetHWindow() );

    if (! pCWnd) pCWnd = CWnd::FromHandle( (HWND) GetHWindow() );     // GetActiveWindow() ?
    if (! pCWnd) pCWnd = CWnd::FromHandle( ::GetActiveWindow() );     // (HWND)GetHWindow()

    progress_object = new CProgressDlg( pCWnd );
    glb_Interrupt = FALSE;

    progress_object->Create(IDD_PROGRESSDLG, pCWnd); // Show dialog
    progress_object->ShowWindow(SW_SHOWNORMAL);      // Show dialog
//    progress_object->ShowWindow(SW_HIDE);      // Show dialog

    ((CProgressCtrl*)progress_object->GetDlgItem( IDC_PROGRESS ))->SetRange32(0, std::max(0, 1));

Message("DONE COLLECTING...");
    ksSetProgressText("Обработка эквидистант...");
    int contours_count = PseudoProcessAcceptedData(&locCurvesPool, 0, false);
//Message("DONE COUNT...");

    if (contours_count == 0)
    {
        progress_object->DestroyWindow();
        progress_object = NULL;
        PumpWaitingMessages();
        Message("Ничего не выбрано или чертеж пуст.");
        return;
    }

#if defined(DO_DEBUG) == 0      //LICENCING!
//MESSAGE(L"LICENCING contours_count = %d" MESSAGE_COMMA contours_count)
    if (contours_count > 10 || m_SelectType == enCurves_All)
    {
        PumpWaitingMessages();
        Message("Данная демо-версия плагина не имеет блока определения\n"
            "множества контуров и будет работать только с 10 контурами\n"
            "или связанными компонентами.\n"
            "Для работы с многими контурами (полнофункциональным плагином)\n"
            "вам необходимо получить лицензию в компании Gkmsoft (gkmsoft.ru)");
        if (m_SelectType == enCurves_All) {
            progress_object->DestroyWindow();
            progress_object = NULL;
            return;
        }
    }
#endif

    ((CProgressCtrl*)progress_object->GetDlgItem( IDC_PROGRESS ))->SetRange32(0, std::max(0, contours_count-1));

    ksStartProgressBar(0, contours_count, "Обработка эквидистант...", 1);
    PseudoProcessAcceptedData(&locCurvesPool, equiparam, false);
//Message("DONE ALL...");
    ksStopProgressBar("Операция завершена.", 1);
    PumpWaitingMessages();

    progress_object->DestroyWindow();
    progress_object = NULL;
}

int CSewingDlg::PseudoProcessAcceptedData(void* pInputData, EquidistantParam *equiparam, bool PseudoGroupMode)
{
    std::vector<CurveType>& locCurvesPool = *(std::vector<CurveType>*)pInputData;
//PARSING TEST
        for (size_t i = 0; i < locCurvesPool.size(); i++)
        {
            unsigned short style = locCurvesPool[i].style;
            unsigned long color = locCurvesPool[i].color;
            double width = locCurvesPool[i].width;
            //std::vector<Edge>& Edges = locCurvesPool[i].Curves;
            size_t nodes_size = locCurvesPool[i].Curves.size();
            for (size_t j = 0; j < locCurvesPool[i].Curves.size(); j++)
            {
                //if (locCurvesPool[i].Curves[j].curve == 0) continue;
                double x1 = locCurvesPool[i].Curves[j].x1;
                double y1 = locCurvesPool[i].Curves[j].y1;
                double x2 = locCurvesPool[i].Curves[j].x2;
                double y2 = locCurvesPool[i].Curves[j].y2;
                reference curve  = locCurvesPool[i].Curves[j].curve;
                int type = GetObjParam(curve, 0, 0, ALLPARAM);
                curve = curve;
            }
        }

    bool contours_left = true;
    int contours_count = 0;
    bool count_mode = (equiparam == 0);

    // optimizing vertices (sorting)
    for (size_t ii0 = 0, contours_left = true; ii0 < locCurvesPool.size(); ii0++, contours_left = true)
    {
/*      struct equi_contour {
        reference contour;
        bool issingle;
        equi_contour(reference r, bool single) : contour(r), issingle(single) {}
        static bool SortedContours (const equi_contour & lthis, const equi_contour & lend) {
            return IsContourInsideContour(lthis.contour, lend.contour); }
      };*/
      std::vector<Edge> CurvesCopy;
      if (count_mode)
        CurvesCopy.insert(CurvesCopy.begin(), locCurvesPool[ii0].Curves.begin(), locCurvesPool[ii0].Curves.end());

      std::vector <equi_contour> equi_contours;
      //std::vector <reference> equi_curves;      // tmp vector

      m_curves_style = 1;   // = locCurvesPool[i].style;
      m_grpAuxScratches = NewGroup(0);
      EndGroup();

      for (size_t ii = 0; contours_left; ii++)    // additional passes to resolve lost branches and draw one contour in a pass (no more than 1+CONTOURS)
      {                                           // where CONTOURS =  (EDGES + COMPONENTS - VERTICES) by Euler formula
/*        struct LineEnd {
            double x; double y;
            size_t index;   // index into Curves (ordered from 1; 0 means UNDEFINED)
            LineEnd(double ux, double uy, size_t uindex) : x(ux), y(uy), index(uindex) {}
            //static bool SortedX (const LineEnd & lthis, const LineEnd & lend) { return lthis.x < lend.x; }
            static bool SortedYpostX (const LineEnd & lthis, const LineEnd & lend) { if (equal_values(lthis.x, lend.x)) return lthis.y < lend.y; return lthis.x < lend.x; }
        };*/

        std::vector<Edge>& LineEnds = count_mode? CurvesCopy : locCurvesPool[ii0].Curves;
        std::vector<LineEnd> EndsX;
//        std::vector<LineEnd> EndsY;
        for (size_t j = 0; j < LineEnds.size(); j++)
        {
            //LineEnds[j].curve = 0;
            LineEnds[j].link_index = LineEnds[j].node_index1 = LineEnds[j].node_index2 = 0;
            LineEnds[j].back_count = LineEnds[j].back_index1 = LineEnds[j].back_index2 = 0;
            double x1 = LineEnds[j].x1;
            double y1 = LineEnds[j].y1;
            EndsX.push_back(LineEnd(x1, y1, (j + 1)));      // (ordered from 1!)
            double x2 = LineEnds[j].x2;
            double y2 = LineEnds[j].y2;
            EndsX.push_back(LineEnd(x2, y2, (j + 1)));      // (ordered from 1!)
        }

        if (ii == 0 && ii0==1)
        {
for (size_t j = 0; j < LineEnds.size(); j++)
{
//    if (LineEnds[j].link_index == 0) 
    double x1 = LineEnds[j].x1, y1 = LineEnds[j].y1;
    double x2 = LineEnds[j].x2, y2 = LineEnds[j].y2;
//if (j+1 == 30 || j+1 == 28 || j+1 == 31)
//    LineSeg(x1+360, y1-5, x2+360, y2-5, 6);
}
        }





//        EndsY.insert(EndsY.begin(), EndsX.begin(), EndsX.end());

        //?std::sort(EndsX.begin(), EndsX.end(), LineEnd::SortedX);
//        std::sort(EndsY.begin(), EndsY.end(), LineEnd::SortedYpostX);
        std::sort(EndsX.begin(), EndsX.end(), LineEnd::SortedYpostX);

        for (size_t j = 0; j < EndsX.size(); j++)
        {
            size_t edge = EndsX[j].index;
            if (edge == 0) continue;
            if (LineEnds[edge - 1].back_count++ == 0)
                LineEnds[edge - 1].back_index1 = (j + 1);
            else LineEnds[edge - 1].back_index2 = (j + 1);
        }

//PARSING SORT
size_t sx = EndsX.size();
//size_t sy = EndsY.size();
size_t sxy = LineEnds.size();
            for (size_t j = 0; j < LineEnds.size(); j++)
            {
                size_t ind1 = LineEnds[j].back_index1;
if (ind1 == 0)
Message("ERROR ind1 == 0");
                double x1 = EndsX[ind1 - 1].x;
                double y1 = EndsX[ind1 - 1].y;
                size_t edge = EndsX[ind1 - 1].index;
                //double x2 = sy? EndsY[j].x : 0;
                //double y2 = sy? EndsY[j].y : 0;
                //size_t ind2 = sy? EndsY[j].index : 0;
                reference curve  = locCurvesPool[ii0].Curves[j].curve;
                curve = curve;
            }

    // detecting nodes among vertices
/*        struct Node {
            double x; double y;
            size_t edge_index;      // (ordered from 1; 0 means UNDEFINED)
            size_t node_index;      // aux field denotes Index of vertex (ordered from 1; 0 means UNDEFINED)
            size_t link_index;      // aux field used later to detect components (ordered from 1; 0 means UNDEFINED)
            Node(double ux, double uy, size_t uindex) : x(ux), y(uy), edge_index(uindex), node_index(0), link_index(0) {}
            Node(double ux, double uy, size_t uindex, size_t uNindex, size_t uLindex) : x(ux), y(uy), edge_index(uindex), node_index(uNindex), link_index(uLindex) {}
        };
        struct NodeLinks {
            Node thisNode;
            size_t edge_count;      // aux count
            size_t parent_index;    // aux index (ordered from 1; 0 means UNDEFINED)
            size_t inner_edge_index;  // aux index (ordered from 1; 0 means UNDEFINED)
            std::vector<Node> Nodes;
            NodeLinks(Node& uNode) : thisNode(uNode.x, uNode.y, uNode.edge_index, uNode.node_index, uNode.link_index),
                                     edge_count(0), parent_index(0), inner_edge_index(0) {}
        };*/
        std::vector<NodeLinks> locNodesPool;

//Message("test input EndsX");
        // checking & proceeding all single-loops
        for (size_t j = 0; j < LineEnds.size(); j++)
        {
            if (equal_points(LineEnds[j].x1, LineEnds[j].y1, LineEnds[j].x2, LineEnds[j].y2, m_precision * m_MmToUnits)) {
                size_t ind1 = LineEnds[j].back_index1 - 1,
                       ind2 = LineEnds[j].back_index2 - 1;
                EndsX[ind1].single_loop = EndsX[ind2].single_loop = 1;
                locNodesPool.push_back(Node(EndsX[ind1].x, EndsX[ind1].y, EndsX[ind1].index));    //, last_node, 0));
                std::vector<Node>& Nodes = locNodesPool.back().Nodes;
                Nodes.push_back(Node(EndsX[ind2].x, EndsX[ind2].y, EndsX[ind2].index));
            }
        }

        bool prev_loaded = false;
        for (size_t j = 0, jprev = 0; j < EndsX.size(); j++)
        {
            if (EndsX[j].single_loop != 0) continue;
            if (/*j == jprev ||*/ prev_loaded == false) {
                //locNodesPool.push_back(Node(EndsX[jprev].x, EndsX[jprev].y, EndsX[jprev].index));
                locNodesPool.push_back(Node(EndsX[j].x, EndsX[j].y, EndsX[j].index));    //, last_node, 0));
                prev_loaded = true;
            }
            else if (equal_points(EndsX[j].x, EndsX[j].y, EndsX[jprev].x, EndsX[jprev].y, m_precision * m_MmToUnits) == false)
                locNodesPool.push_back(Node(EndsX[j].x, EndsX[j].y, EndsX[j].index));    //, last_node, 0));
            else
            {
                //node found
                Node& lastNode = locNodesPool.back().thisNode;
                if (equal_points(lastNode.x, lastNode.y, EndsX[j].x, EndsX[j].y, m_precision * m_MmToUnits) == false)
                {
                    locNodesPool.push_back(Node(EndsX[j].x, EndsX[j].y, EndsX[j].index));    //, last_node, 0));  // MUST BE NOT
                }
                else
                {
                    std::vector<Node>& Nodes = locNodesPool.back().Nodes;
                    Nodes.push_back(Node(EndsX[j].x, EndsX[j].y, EndsX[j].index));
                }
            }
            jprev = j;
        }

//PARSING NODES
size_t sn = locNodesPool.size();
int linesegs = 0;
            for (size_t j = 0; j < locNodesPool.size(); j++)
            {
                Node& node = locNodesPool[j].thisNode;
                double x0 = node.x;
                double y0 = node.y;
                size_t ind0 = node.edge_index;
                std::vector<Node>& Nodes = locNodesPool[j].Nodes;
size_t snodes = Nodes.size();
  if (snodes == 0) linesegs ++;
                for (size_t jj = 0; jj < Nodes.size(); jj++)
                {
                    double x1 = Nodes[jj].x;
                    double y1 = Nodes[jj].y;
                    size_t ind1 = Nodes[jj].edge_index;
                    ind1 = ind1;
                }
            }

    // detecting components in the graph
        std::vector <size_t> link_map;
        std::map <size_t, size_t> comp_map;
        std::map <size_t, size_t> :: iterator map_Iter;
        typedef std::pair <size_t, size_t> KeyValPair;
        for (size_t j = 0; j < locNodesPool.size(); j++)
        {
            Node& node = locNodesPool[j].thisNode;
            node.node_index = (j + 1);      // (ordered from 1!)
            size_t ind0 = node.edge_index - 1;  // (ordered from 1!)
            size_t comp_index = node.node_index;
            if (LineEnds[ind0].link_index == 0)
                LineEnds[ind0].link_index = comp_index;
            else
                comp_index = LineEnds[ind0].link_index;
            if (node.link_index == 0) node.link_index = comp_index;
            if (comp_map.count(comp_index) == 0)
            {
                bool need_insert = true;
                if (node.node_index != comp_index && comp_index <= link_map.size())      // special case!
                {
                    size_t index = link_map[ comp_index - 1 ],
                           prev_index = 0;
                    while ( comp_map.find(index) == comp_map.end() && index != prev_index )
                    {
                        prev_index = index;
                        index = link_map[index - 1];
                    }
                    map_Iter = comp_map.find(index);
                    if (map_Iter != comp_map.end() && prev_index != 0)
                    {
                        map_Iter->second = node.node_index;
                        comp_index = node.link_index = index;
                        need_insert = false;
                    }
                }
                if (need_insert) comp_map.insert(KeyValPair(node.link_index, node.node_index));
            }
            //else { map_Iter = comp_map.find(node.link_index); }

            std::vector<Node>& Nodes = locNodesPool[j].Nodes;
            for (size_t jj = 0; jj < Nodes.size(); jj++)
            {
                size_t ind1 = Nodes[jj].edge_index - 1;     // (ordered from 1!)
                if (LineEnds[ind1].link_index == 0)
                    LineEnds[ind1].link_index = comp_index;
                else
                    if (LineEnds[ind1].link_index != comp_index)
                    {   // merging bushes with different comp_index by help of the map
                        size_t comp_index1 = LineEnds[ind1].link_index;
                        size_t comp_index0 = std::min(comp_index, comp_index1);
                        map_Iter = comp_map.find(comp_index);
                        if (map_Iter != comp_map.end() && comp_index != comp_index0) comp_map.erase(comp_index);
                        map_Iter = comp_map.find(comp_index1);
                        if (map_Iter != comp_map.end() && comp_index1 != comp_index0) comp_map.erase(comp_index1);
                        map_Iter = comp_map.find(comp_index0);
                        bool need_insert = true;
                        if (map_Iter == comp_map.end())
                        {
                            if (node.node_index != comp_index0 && comp_index0 <= link_map.size())      // special case!
                            {
                                size_t index = link_map[ comp_index0 - 1 ],
                                       prev_index = 0;
                                while ( comp_map.find(index) == comp_map.end() && index != prev_index )
                                {
                                    prev_index = index;
                                    index = link_map[index - 1];
                                }
                                map_Iter = comp_map.find(index);
                                if (map_Iter != comp_map.end())
                                {
                                    comp_index0 = node.link_index = index;
                                    need_insert = false;
                                }
                            }
                        }
                        if (link_map.size() >= comp_index) {
                            size_t next_link = link_map[comp_index - 1];
                            link_map[comp_index - 1] = node.node_index;
                            if (link_map.size() >= next_link) link_map[next_link - 1] = node.node_index;
                        }
                        if (link_map.size() >= comp_index1) {
                            size_t next_link = link_map[comp_index1 - 1];
                            link_map[comp_index1 - 1] = node.node_index;
                            if (link_map.size() >= next_link) link_map[next_link - 1] = node.node_index;
                        }
                        if (need_insert) comp_map.insert(KeyValPair(comp_index0, node.node_index));
                        else
                            map_Iter->second = node.node_index;
                        node.link_index = comp_index = comp_index0;
                        LineEnds[ind1].link_index = comp_index;
                        LineEnds[ind0].link_index = comp_index;
                    }
            }
            if (link_map.size() < node.node_index) link_map.push_back(node.link_index);     // same as comp_index here
            else link_map[node.node_index - 1] = node.link_index;
        }

    // writing components' indices to Nodes
        for (size_t j = 0; j < locNodesPool.size(); j++)
        {
            Node& node = locNodesPool[j].thisNode;
            size_t index = node.link_index,
                   prev_index = 0;
//if (index != link_map[ j ])
//Message("MAY BE DIFFERENT HERE!");
            index = link_map[ j ];
            while ( comp_map.find(index) == comp_map.end() && index != prev_index )
            {
                prev_index = index;
                index = link_map[index - 1];
            }
            if (comp_map.find(index) != comp_map.end() && prev_index != 0)
            {
                size_t new_index = index;
                index = node.link_index;
                prev_index = 0;
                while (comp_map.find(index) == comp_map.end() && index != prev_index )
                {
                    prev_index = index;
                    size_t tmp_index = link_map[index - 1];
                    link_map[index - 1] = new_index;
                    index = tmp_index;
                }
                link_map[ j ] = node.link_index = new_index;
            }
        }

        for (size_t j = 0; j < locNodesPool.size(); j++)
        {
            Node& node = locNodesPool[j].thisNode;
            size_t new_index = link_map[ j ];
            if (node.link_index != new_index) node.link_index = new_index;
            //std::vector<Edge>& LineEnds = locCurvesPool[i].Curves;
            if (node.edge_index != 0)
            {
                size_t index = node.edge_index - 1;
                if (LineEnds[index].link_index != new_index) LineEnds[index].link_index = new_index;
            }
        }

//** Here -
//**    EDGES = LineEnds.size();
//**    VERTICES = locNodesPool.size();
//**    COMPONENTS = comp_map.size();
//**    Contours =  (EDGES + COMPONENTS - VERTICES);

#if defined(DO_DEBUG)
//MESSAGE(L"TEST")
#endif
    // separating from the mesh the different branches and contours to prepare the drawing
        for (size_t j = 0; j < locNodesPool.size(); j++)
        {
            Node& node = locNodesPool[j].thisNode;
            //std::vector<Edge>& LineEnds = locCurvesPool[i].Curves;
            if (node.edge_index != 0)
            {
                size_t index = node.edge_index - 1;
                if (LineEnds[index].node_index1 == 0) LineEnds[index].node_index1 = node.node_index;
                else
                    if (LineEnds[index].node_index2 == 0) LineEnds[index].node_index2 = node.node_index;
                    //else Message("ERROR");
            }
            std::vector<Node>& Nodes = locNodesPool[j].Nodes;
            for (size_t jj = 0; jj < Nodes.size(); jj++)
            {
                if (Nodes[jj].edge_index != 0)
                {
                    size_t index = Nodes[jj].edge_index - 1;
                    if (LineEnds[index].node_index1 == 0) LineEnds[index].node_index1 = node.node_index;
                    else
                        if (LineEnds[index].node_index2 == 0) LineEnds[index].node_index2 = node.node_index;
                        //else Message("ERROR");
                }
            }

            locNodesPool[j].edge_count = 1 + locNodesPool[j].Nodes.size();
            locNodesPool[j].parent_index = (1 + j);      // (ordered from 1!)
        }

//MESSAGE(L"TEST")
//PARSE!
    for (size_t j = 0; j < LineEnds.size(); j++)
    {
        if (LineEnds[j].node_index1 == 0 || LineEnds[j].node_index2 == 0)
        {
size_t ni1 = LineEnds[j].node_index1;
size_t ni2 = LineEnds[j].node_index2;
MESSAGE(L"ERROR edge %d:\n\n ni1 = %d; ni2 = %d" MESSAGE_COMMA j+1 MESSAGE_COMMA ni1 MESSAGE_COMMA ni2)
        }
        if (LineEnds[j].node_index1 == LineEnds[j].node_index2)
        {
size_t ni1 = LineEnds[j].node_index1;
size_t ni2 = LineEnds[j].node_index2;
//MESSAGE(L"WARN (suspicion NOT A LINESEG?) edge %d:\n\n ni1 = %d; ni2 = %d" MESSAGE_COMMA j+1 MESSAGE_COMMA ni1 MESSAGE_COMMA ni2)
        }
    }
/*    for (size_t j = 0; j < locNodesPool.size(); j++)
if (locNodesPool[j].thisNode.node_index == 188 || locNodesPool[j].thisNode.node_index == 208)
{
    double x1 = locNodesPool[j].thisNode.x, y1 = locNodesPool[j].thisNode.y;
    Point(x1, y1, 3);
}*/

        typedef std::map <size_t,size_t> Branches;      // indices into locNodesPool
        Branches locBranchesPool,
                 locBranchesBegs;
        std::map <size_t,size_t> locEdgesPool;          // indices into LineEnds
        std::map <size_t,size_t> locEdgesRegistered;    // back-indices into LineEnds

        // first shear the bush (take away all branches to left a pure mesh of contours
        std::vector<NodeLinks> locNodesPrev(locNodesPool);
        std::vector<NodeLinks> locNodesWork;
        do {
            size_t prev_size = locBranchesPool.size();
            for (size_t j = 0; j < locNodesPrev.size(); j++)
            {
                Node& node = locNodesPrev[j].thisNode;
                std::vector<Node>& Nodes = locNodesPrev[j].Nodes;
                size_t parent_index = locNodesPrev[j].parent_index;

                if (locNodesPool[parent_index - 1].edge_count > 1) locNodesWork.push_back(locNodesPrev[j]);
                else    // if (locNodesPrev[j].edge_count <= 1)     // same as Nodes.size() == 0
                {
                    size_t inner_edge_index = locNodesPool[parent_index - 1].inner_edge_index;
                    size_t node_index = node.node_index;
                    size_t edge_index = inner_edge_index == 0? node.edge_index : Nodes[inner_edge_index - 1].edge_index;

                    while (locEdgesRegistered.find(edge_index) != locEdgesRegistered.end())
                    {
                        if (inner_edge_index >= Nodes.size()) break;
                        inner_edge_index++;
                        edge_index = Nodes[inner_edge_index - 1].edge_index;
                    }

//                    if (edge_index > 0)     // MUST BE
                    if (LineEnds[edge_index - 1].node_index1 == node_index)
                        node_index = LineEnds[edge_index - 1].node_index2;
                    else node_index = LineEnds[edge_index - 1].node_index1;

                    bool need_relink = false;
//                    if (node_index > 0)     // MUST BE
                        if (locNodesPool[node_index - 1].edge_count > 1)
                        {
                            Node& node0 = locNodesPool[node_index - 1].thisNode;
                            std::vector<Node>& Nodes0 = locNodesPool[node_index - 1].Nodes;
                            size_t inner_edge_index = locNodesPool[node_index - 1].inner_edge_index;
                            size_t edge_index0 = inner_edge_index == 0? node0.edge_index : Nodes0[inner_edge_index - 1].edge_index;

                            if (edge_index0 == edge_index) locNodesPool[node_index - 1].inner_edge_index++;
                            locNodesPool[node_index - 1].edge_count--;
                            //edge_index = edge_index0;
                        }
                        else need_relink = true;

//if (need_relink && edge_index == 4 && i == 0)
//Message("GOTCHA need_relink!");
      if (locEdgesRegistered.find(edge_index) == locEdgesRegistered.end())
      {
                    //need_relink = need_relink && (locBranchesPool.count(node.node_index) != 0);
                    need_relink = need_relink && (locBranchesBegs.count(node_index) != 0);
                    if (need_relink == false) {
                        locBranchesPool.insert(KeyValPair(node.node_index, node_index));
                        //locBranchesPool.insert(KeyValPair(node.node_index, edge_index));
                        locBranchesBegs.insert(KeyValPair(node_index, node.node_index));     // aux map to seek for branch beginnings
                    }
      }

                    //if (locEdgesRegistered.find(edge_index) == locEdgesRegistered.end())
                    if (need_relink == false)
                    {
                        locEdgesPool.insert(KeyValPair(node.node_index, edge_index));
                        locEdgesRegistered.insert(KeyValPair(edge_index, node.node_index));
                    }

//if (need_relink && i == 0)
//Message("GOTCHA need_relink!");
                    if (need_relink
                        && locBranchesBegs.count(node.node_index) != 0 && locBranchesBegs.count(node_index) != 0
                        && (locBranchesBegs.find(node.node_index)->second != node_index)
                        && (locBranchesBegs.find(node_index)->second != node.node_index))
                    {
                        size_t prev_index = node_index;
                        size_t node_index = node.node_index;
                        size_t new_index = locBranchesBegs.find(node_index)->second;
//                                locBranchesPool.erase(node.node_index);
                        if (prev_index != new_index) {
                            locBranchesBegs.erase(node_index);
                            locBranchesBegs.insert(KeyValPair(node_index, prev_index));
                        }
                        if (locBranchesPool.count(prev_index) != 0) locBranchesPool.erase(prev_index);
                        locBranchesPool.insert(KeyValPair(prev_index, node_index));
                        //if (locNodesPool[node_index - 1].edge_count > 1)
                        size_t etmp_index = edge_index;
                        if (locNodesPool[node_index - 1].Nodes.size() > 0)
                        do {
                            Node& node0 = locNodesPool[node_index - 1].thisNode;
                            std::vector<Node>& Nodes0 = locNodesPool[node_index - 1].Nodes;
                            size_t& inner_edge_index = locNodesPool[node_index - 1].inner_edge_index;
                            size_t edge_index0 = inner_edge_index == 0? node0.edge_index : Nodes0[inner_edge_index - 1].edge_index;

                            if (edge_index0 == edge_index) inner_edge_index++;
                            if (inner_edge_index > Nodes0.size()) inner_edge_index = 0;
                            etmp_index = edge_index0;
                        } while (etmp_index == edge_index);
                        //size_t new_index0 = 0;

                        if (new_index != prev_index)
                            locEdgesPool.erase(prev_index);
                        if (locEdgesPool.count(prev_index) == 0)
                            locEdgesPool.insert(KeyValPair(prev_index, edge_index));
                        if (locEdgesRegistered.count(edge_index) != 0) locEdgesRegistered.erase(edge_index);
                        locEdgesRegistered.insert(KeyValPair(edge_index, prev_index));

                        do {
                            locBranchesPool.erase(new_index);
                            locBranchesPool.erase(node_index);
                            locBranchesPool.insert(KeyValPair(node_index, new_index));
                            if (new_index != prev_index)
                                locEdgesPool.erase(node_index);
                            if (locEdgesPool.count(node_index) == 0)
                                locEdgesPool.insert(KeyValPair(node_index, etmp_index));
                            if (locEdgesRegistered.count(etmp_index) != 0) locEdgesRegistered.erase(etmp_index);
                            locEdgesRegistered.insert(KeyValPair(etmp_index, node_index));
                            prev_index = node_index;
                            node_index = new_index;
                            if (locBranchesBegs.count(node_index) == 0)
                            {
                                locBranchesBegs.erase(new_index);
                                locBranchesBegs.insert(KeyValPair(node_index, prev_index));
                                break;
                            }
                            new_index = locBranchesBegs.find(node_index)->second;
                            if (locEdgesPool.count(new_index) != 0)
                                etmp_index = locEdgesPool.find(new_index)->second;
//                            locBranchesBegs.erase(new_index);
//                            locBranchesBegs.insert(KeyValPair(new_index, node_index));
                            locBranchesBegs.erase(node_index);
                            locBranchesBegs.insert(KeyValPair(node_index, prev_index));
                        }
                        while (1);//(locBranchesBegs.count(new_index) != 0);      // && new_index0 != 0);
                    }
                }
            }
            //if (locNodesWork.size() == locNodesPrev.size()) break;
            if (prev_size == locBranchesPool.size()) break;
            locNodesPrev.clear();
            locNodesPrev.insert(locNodesPrev.begin(), locNodesWork.begin(), locNodesWork.end());
            locNodesWork.clear();
        } while (locNodesPrev.size() > 0);

        locNodesWork.clear();
        //locEdgesRegistered.clear();

for (size_t j = 0; j < locNodesPrev.size(); j++)
{
    Node& node = locNodesPrev[j].thisNode;
    double x = node.x, y = node.y;
/*    if (m_CurrentViewIndex != 0)
    {
        SetViewMtr(m_CurrentViewIndex,true);
        ksPointFromMtr (x, y, &x, &y);
        _DeleteMtr(), _DeleteMtr();
    }*/
//    Point(x, y+0, 5);
}

size_t BRunches0 = locBranchesPool.size();      // all bare branches
size_t BRunches1 = locNodesPrev.size();         // all contours
//MESSAGE(L"Nodes \n\nBRunches0 = %d\nBRunches1 = %d" MESSAGE_COMMA BRunches0 MESSAGE_COMMA BRunches1)

        std::map <size_t,size_t> DrawnEdges;      // indices into LineEnds to register all drawn edges (find the lost ones)

        ksDocument2DPtr m_pKompasDoc5 = kompasAPI->ActiveDocument2D();  // Current Kompas document

        IKompasDocumentPtr doc7 = newKompasAPI->ActiveDocument; // ksTransferReference( m_pKompasDoc5->reference, 0 );
        IKompasDocument2DPtr pkdDocument = 0;
        doc7->QueryInterface(&pkdDocument);
        IViewPtr ivpView = pkdDocument->ViewsAndLayersManager->Views->View[0];
        IDrawingContainerPtr pdcContainer = 0;
        ivpView->QueryInterface(&pdcContainer);

        bool contour_state = false;

        equi_curves.clear();

        // second we draw the bush branches left after shearing
        while (locBranchesPool.size() > 0)
        {
            IDrawingContoursPtr idcContours = pdcContainer->DrawingContours;
            IDrawingObjectPtr idoObject = 0;
            IDrawingContourPtr idcContour = 0;
            IContourPtr pContour = 0;

            map_Iter = locBranchesPool.begin();
            size_t index = map_Iter->first;
            size_t index0 = index;
            while (locBranchesBegs.find(index) != locBranchesBegs.end())
            {
                index = locBranchesBegs.find(index)->second;
                if (locBranchesPool.count(index) != 0)
                    if (index0 != index) continue;
                    else break;

                std::map <size_t, size_t> :: iterator test_Iter = map_Iter;
                if (++test_Iter == locBranchesPool.end())
                {
                    break;    // BE NEVER but may occur once per each component!
                }
                else map_Iter = test_Iter;
                index = map_Iter->first;
                index0 = index;
            }
            if (locBranchesPool.count(index) == 0) break;       // still may occur ?! NEVER now

            map_Iter = locBranchesPool.find(index);
            index = map_Iter->second;
            size_t tmp_index = map_Iter->first;
            size_t etmp_index = 0;
            if (locEdgesPool.count(map_Iter->first) > 0)
                etmp_index = locEdgesPool.find(map_Iter->first)->second;
            if (etmp_index != 0)
                index = (LineEnds[etmp_index - 1].node_index1 == tmp_index)? LineEnds[etmp_index - 1].node_index2 : LineEnds[etmp_index - 1].node_index1;
            //?  index = (LineEnds[index - 1].node_index1 == tmp_index)? LineEnds[index - 1].node_index2 : LineEnds[index - 1].node_index1;
            double x1 = locNodesPool[map_Iter->first - 1].thisNode.x;
            double y1 = locNodesPool[map_Iter->first - 1].thisNode.y;
            double x2 = locNodesPool[index - 1].thisNode.x;     // locNodesPool[map_Iter->second - 1].thisNode.x;
            double y2 = locNodesPool[index - 1].thisNode.y;     // locNodesPool[map_Iter->second - 1].thisNode.y;
            reference curve = (etmp_index == 0)? 0 : LineEnds[etmp_index - 1].curve;

            // setting color for page
            {
                unsigned short style = locCurvesPool[ii0].style;
                unsigned long color = locCurvesPool[ii0].color;
                double width = locCurvesPool[ii0].width;

/*                SetCurrentColor(color);
                double widthTuning = (m_CurrentViewScale > 0.5)? 0.5 / m_CurrentViewScale : 1.0;      // this isn't nessessary but makes drawins more fine and readable!
                widthTuning = 1.0;      // better for stamps etc. / views be with widthTuning != 1?
                if (PseudoGroupMode == true &&
                    (m_SaveViewMatricesPdfMode == 0 || m_GenObject != 0) && m_CurrentViewIndex != 0)
                    SetCurrentLineWidth(width * m_CurrentViewScale * widthTuning);
                else
                    SetCurrentLineWidth(width * widthTuning);*/
            }

            bool needStroke = false;
            while (etmp_index != 0)     //while (1) // while (map_Iter != locBranchesPool.end())
            {
//PseudoGroupMode = false;
/*                if (PseudoGroupMode == false)
                    if (needStroke == false) m_pPainter->MoveTo(x1, y1);*/
//int bbb =  ExistGroupObj(rGroup); 
                if ( DrawnEdges.count(etmp_index) == 0 )  //? doesn't work
                {
                    bool accepted = true;
                    if (PseudoGroupMode == false)
                    {
                        m_issingle = (contour_state == false);
                        if (contour_state == false)
                        {
//Message("0");
                            contour_state = true;
                            m_BegPoint.x = x1, m_BegPoint.y = y1;
                            m_swapped = CheckSwapCurve(curve);
                            equi_curves.clear();
                            equi_curves_count = 0;
                        }
                        if (m_ProcessClosedOnly == 0 && count_mode == false)
                        {
                            reference obj = 0;
                            m_aux_scratch = 0;
//Message("0 - branch");
                            obj = Draw2DElement(curve);
                            if (m_issingle && m_aux_scratch == 0)
                                idoObject = ksTransferReference(obj, 0);
                            else if (obj) {
                                m_issingle = false;
                                if (idcContour == 0) {
                                    //Contour(1);
                                    //m_pKompasDoc5->ksContour(1);
                                    idcContour = idcContours->Add();
                                    idcContour->QueryInterface(&pContour);
                                }
                                if (idoObject) {
                                    pContour->CopyCurve(idoObject, FALSE);
                                    idoObject->Delete(); idoObject = 0;
                                }
                                IDrawingObjectPtr pCurve = 0;
                                if (m_aux_scratch) {
                                    pCurve = ksTransferReference(m_aux_scratch, 0);
                                    if (pCurve) pContour->CopyCurve(pCurve, FALSE);
                                    m_aux_scratch = 0;
                                }
                                pCurve = obj? ksTransferReference(obj, 0) : 0;
                                if (pCurve) pContour->CopyCurve(pCurve, FALSE);
                                if (obj) DeleteObj(obj);
                            }
                            equi_curves.push_back(curve);
                            equi_curves_count++;
                        }
                    }
                    else if (etmp_index > 0)
                    {
/*                        m_PseudoGroup.push_back(LineEnds[etmp_index - 1].curve);
                        m_PseudoGroupStartPoints.push_back(std::pair<double,double>(x1,y1));*/
                    }
                    needStroke = needStroke || accepted;

                    /*? doesn't work
                        DrawnEdges.insert(KeyValPair(etmp_index,map_Iter->first));
                        locEdgesRegistered.erase(etmp_index);
                    }*/
                }
                else if (needStroke) {
                    needStroke = false;
                    if (PseudoGroupMode == false) {   // m_pPainter->Stroke();

                        if (contour_state == true)
                        {
                            contour_state = false;
                            if (m_ProcessClosedOnly == 0)
                            {
                                if (contours_count++, count_mode == false)
                                {
                                    //reference contour = EndObj();
                                    //reference contour = m_pKompasDoc5->ksEndObj();
                                    if (pContour) {
                                        int pcount = pContour->Count;
                                        if (pContour->Count > 0)
                                        {
                                            idcContour->Update();
                                            PumpWaitingMessages();
                                        }
                                    }
#if defined(DO_DEBUG)
//Message("1");
#endif
                                    reference contour = idoObject? idoObject->Reference : idcContour->Reference;

                                    //equi_contours.push_back(equi_contour(contour, issingle, m_swapped, equi_curves));

                                    if (!count_mode) equiparam->geoObj = contour;
                                    if (TestInterrupt("Прервать операцию?", contours_count)) return contours_count;

                                    /*if (Equidistant(equiparam) == 0) //параметры эквидистанты
                                        Message("Error drawing Equidistant");*/
                                    EquidistantParam loc_equiparam = *equiparam;
                                    //if (DrawEquidistant(&loc_equiparam, m_swapped) == 0) //параметры эквидистанты
                                    if (m_issingle == false) idoObject = idcContour;
                                    if (DrawEquidistant7(idoObject, &loc_equiparam, m_swapped) == 0) //параметры эквидистанты
                                        Message("Error drawing Equidistant");
                                    if (equi_curves.size() == 0) ProcessMarks(contour, 0, &loc_equiparam);
                                    else
                                        for (int i = 0; i < equi_curves.size(); i++)
                                            ProcessMarks(equi_curves[i], contour, &loc_equiparam);

                                    equi_curves.clear();

                                    //if (contour) DeleteObj(contour);
                                    if (idcContour) idcContour->Delete();
                                    if (idoObject) idoObject->Delete();
                                }
                            }
                        }
                    } else {
/*                        DrawProtoContour2(false);
                        m_PseudoGroup.clear();
                        m_PseudoGroupStartPoints.clear();*/
                    }
                }

                std::map <size_t, size_t> :: iterator imap = locEdgesPool.find(map_Iter->first);
                if (needStroke && imap != locEdgesPool.end()) {
                    DrawnEdges.insert(KeyValPair(imap->second,imap->first));
                    locEdgesRegistered.erase(imap->second);
                }
                locBranchesBegs.erase(map_Iter->first);
                locBranchesPool.erase(map_Iter->first);
                map_Iter = locBranchesPool.find(index);
                if (map_Iter == locBranchesPool.end()) break;
                index = map_Iter->second;
                size_t tmp_index = map_Iter->first;
                /*size_t*/ etmp_index = 0;          //! BUG in C++ (while(etmp_index) ABOVE is misunderstood with this declaration)
                if (locEdgesPool.count(map_Iter->first) > 0)
                    etmp_index = locEdgesPool.find(map_Iter->first)->second;
                if (etmp_index != 0)
                    index = (LineEnds[etmp_index - 1].node_index1 == tmp_index)? LineEnds[etmp_index - 1].node_index2 : LineEnds[etmp_index - 1].node_index1;
                //? index = (LineEnds[index - 1].node_index1 == tmp_index)? LineEnds[index - 1].node_index2 : LineEnds[index - 1].node_index1;
                x1 = locNodesPool[map_Iter->first - 1].thisNode.x;
                y1 = locNodesPool[map_Iter->first - 1].thisNode.y;
                x2 = locNodesPool[index - 1].thisNode.x;
                y2 = locNodesPool[index - 1].thisNode.y;
                curve = (etmp_index == 0)? 0 : LineEnds[etmp_index - 1].curve;
            }
            if (needStroke)
                if (PseudoGroupMode == false) {     // m_pPainter->Stroke();
                    if (contour_state == true)
                    {
                        contour_state = false;
                        if (m_ProcessClosedOnly == 0)
                        {
                            if (contours_count++, count_mode == false)
                            {
                                //reference contour = EndObj();
                                //reference contour = m_pKompasDoc5->ksEndObj();
                                if (pContour) {
                                    int pcount = pContour->Count;
                                    if (pContour->Count > 0)
                                    {
                                        idcContour->Update();
                                        PumpWaitingMessages();
                                    }
                                }
#if defined(DO_DEBUG)
//Message("2");
#endif
                                reference contour = idoObject? idoObject->Reference : idcContour->Reference;

                                //equi_contours.push_back(equi_contour(contour, issingle, m_swapped, equi_curves));

                                if (!count_mode) equiparam->geoObj = contour;
                                if (TestInterrupt("Прервать операцию?", contours_count)) return contours_count;

                                /*if (Equidistant(equiparam) == 0) //параметры эквидистанты
                                    Message("Error drawing Equidistant");*/
                                EquidistantParam loc_equiparam = *equiparam;
                                //if (DrawEquidistant(&loc_equiparam, m_swapped) == 0) //параметры эквидистанты
                                if (m_issingle == false) idoObject = idcContour;
                                if (DrawEquidistant7(idoObject, &loc_equiparam, m_swapped) == 0) //параметры эквидистанты
                                    Message("Error drawing Equidistant");
                                if (equi_curves.size() == 0) ProcessMarks(contour, 0, &loc_equiparam);
                                else
                                    for (int i = 0; i < equi_curves.size(); i++)
                                        ProcessMarks(equi_curves[i], contour, &loc_equiparam);

                                equi_curves.clear();

                                //if (contour) DeleteObj(contour);
                                if (idcContour) idcContour->Delete();
                                if (idoObject) idoObject->Delete();
                            }

    /*                        RefreshKompasActiveDocument();
                            ksExecuteKompasCommand(ksCMRefresh, 1);
                            kompasAPI->ksPumpWaitingMessages();*/
                        }
                    }
                } else {
/*                    DrawProtoContour2(false);
                    m_PseudoGroup.clear();
                    m_PseudoGroupStartPoints.clear();*/
                }
        }

        typedef std::map <size_t,size_t> Contours;      // indices into locNodesPool
        Contours locContoursPool,
                 locContoursEdges,
                 locContoursPassed;

        locEdgesRegistered.clear();

        // finally draw the contours by pulling together all closed paths
        for (size_t j = 0; j < locNodesPrev.size(); j++)
        {
            NodeLinks& node_elem = locNodesPool[locNodesPrev[j].parent_index - 1];
            locContoursPool.insert(KeyValPair(locNodesPrev[j].thisNode.node_index, locNodesPrev[j].parent_index));
            locContoursEdges.insert(KeyValPair(locNodesPrev[j].thisNode.node_index, 0));
            node_elem.inner_edge_index = 0;
            node_elem.edge_count = 1 + node_elem.Nodes.size();    // = locNodesPrev[j].edge_count;
        }

        std::vector <size_t> edge_map;

//MESSAGE(L"DRAW CONT")
        while (locContoursPool.size() > 0)
        {
            IDrawingContoursPtr idcContours = pdcContainer->DrawingContours;
            //IDrawingObjectPtr idoObject = 0;
            IDrawingContourPtr idcContour = 0;
            IContourPtr pContour = 0;

            bool need_break = false;
            map_Iter = locContoursPool.begin();
            locContoursPassed.clear();
            locContoursPassed.insert(KeyValPair(map_Iter->first, 0));

            //bool issingle = false;
            m_issingle = false;
            reference singleton = 0;
            double beg_x1, beg_y1, beg_x2, beg_y2;

            size_t node_index0 = map_Iter->first;
            size_t parent_index = map_Iter->second;

            size_t node_index = node_index0,
                   prev_index = node_index0;
            do {
                map_Iter = locContoursPool.find(node_index);
                if (need_break || map_Iter == locContoursPool.end()) {need_break = true; break;}   // ?NEVER
                parent_index = map_Iter->second;
                Node& node = locNodesPool[parent_index - 1].thisNode;
                std::vector<Node>& Nodes = locNodesPool[parent_index - 1].Nodes;
                size_t& inner_edge_index = locNodesPool[parent_index - 1].inner_edge_index;

                size_t last_index = prev_index;
                size_t edge_index = 0;
                //bool need_break = false;
                do {
                    last_index = prev_index;
                    prev_index = node_index = node.node_index;    // == node_index0
                    do {
                        if (inner_edge_index > Nodes.size()) {need_break = true; break;}
                        edge_index = (inner_edge_index == 0)? node.edge_index : Nodes[inner_edge_index - 1].edge_index;
                        inner_edge_index++;
                    } while (DrawnEdges.count(edge_index) != 0);
                    if (need_break) break;
    //                if (edge_index > 0)     // MUST BE
                    if (LineEnds[edge_index - 1].node_index1 == node_index)
                           node_index = LineEnds[edge_index - 1].node_index2;
                    else node_index = LineEnds[edge_index - 1].node_index1;
                } while (inner_edge_index <= Nodes.size() &&
                    ( (node_index != node_index0 && locContoursPool.find(node_index) == locContoursPool.end())
                 || ( last_index == node_index && locEdgesRegistered.count(edge_index) != 0 ) ));
                 //|| (last_index == node_index && (locContoursEdges.count(prev_index) != 0 && locContoursEdges.find(prev_index)->second != 0)) ));

                if (need_break) break;      // no cycle found

                if (locContoursEdges.count(prev_index) != 0)
                    locContoursEdges.find(prev_index)->second = edge_index;
                locEdgesRegistered.insert(KeyValPair(edge_index, 0));

                if (locContoursPassed.find(node_index) == locContoursPassed.end())
                {
                    if (locContoursPassed.count(prev_index) != 0)
                        locContoursPassed.find(prev_index)->second = node_index;
                    locContoursPassed.insert(KeyValPair(node_index, 0));
                    continue;
                }

                m_issingle = locContoursPassed.size() == 1;
                singleton = 0;

                map_Iter = locContoursPassed.find(node_index);
                prev_index = node_index0 = map_Iter->first;
                node_index = map_Iter->second;
                while (locContoursPassed.size() > 0 && map_Iter != locContoursPassed.end())    // && node_index != 0)
                {
                    prev_index = map_Iter->first;
                    node_index = map_Iter->second;

                    double x1 = locNodesPool[prev_index - 1].thisNode.x;
                    double y1 = locNodesPool[prev_index - 1].thisNode.y;
                    double x2 = locNodesPool[(node_index == 0? node_index0 : node_index) - 1].thisNode.x;
                    double y2 = locNodesPool[(node_index == 0? node_index0 : node_index) - 1].thisNode.y;
                    if (m_issingle && prev_index == node_index0)
                        x2 = locNodesPool[prev_index - 1].Nodes[0].x,
                        y2 = locNodesPool[prev_index - 1].Nodes[0].y;

                    map_Iter = locContoursPassed.find(node_index);

                    if (prev_index == node_index0)
                    {
                        // setting color for page
                        {
                            unsigned short style = locCurvesPool[ii0].style;
                            unsigned long color = locCurvesPool[ii0].color;
                            double width = locCurvesPool[ii0].width;

/*                            SetCurrentColor(color);
                            double widthTuning = (m_CurrentViewScale > 0.5)? 0.5 / m_CurrentViewScale : 1.0;      // this isn't nessessary but makes drawins more fine and readable!
                            widthTuning = 1.0;      // better for stamps etc. / views be with widthTuning != 1?
                            if (PseudoGroupMode == true &&
                                (m_SaveViewMatricesPdfMode == 0 || m_GenObject != 0) && m_CurrentViewIndex != 0)
                                SetCurrentLineWidth(width * m_CurrentViewScale * widthTuning);
                            else
                                SetCurrentLineWidth(width * widthTuning);*/
                        }
/*                        if (PseudoGroupMode == false)
                            m_pPainter->MoveTo(x1, y1);*/
                    }

                    if (locContoursEdges.count(prev_index) != 0)
                        edge_index = locContoursEdges.find(prev_index)->second;
                    reference curve = (edge_index == 0)? 0 : LineEnds[edge_index - 1].curve;
                    //if ( DrawnEdges.count(etmp_index) == 0 )  //? doesn't work
                    if (PseudoGroupMode == false)
                    {
                        if (contour_state == false)
                        {
                            contour_state = true;
                            m_BegPoint.x = x1, m_BegPoint.y = y1;
                            beg_x1 = x1, beg_y1 = y1;
                            beg_x2 = x2, beg_y2 = y2;
//Message("000000000");
                            m_swapped = CheckSwapCurve(curve);
                            equi_curves.clear();
                            equi_curves_count = 0;
                            //if (m_issingle) {
                            //    int type = GetObjParam(curve, 0, 0, ALLPARAM);
                            //    m_issingle = (type != CONTOUR_OBJ);
                            //}
                            //if (!issingle) Contour(1);
                            //if (!m_issingle) m_pKompasDoc5->ksContour(1);
                        }
                        reference obj = 0;
                        m_aux_scratch = 0;
                        if (count_mode == false)
                        {
//int type = GetObjParam(curve, 0, 0, ALLPARAM);
//bool iscontour = type == CONTOUR_OBJ;
                            if (m_issingle && m_aux_scratch == 0) singleton = curve;
                            else {
//Message("00000 - contour");
                                obj = Draw2DElement(curve, m_issingle);
                                if (obj) {
                                    m_issingle = false;
                                    if (idcContour == 0) {
                                        //Contour(1);
                                        //m_pKompasDoc5->ksContour(1);
                                        idcContour = idcContours->Add();
                                        idcContour->QueryInterface(&pContour);
                                    }
                                    IDrawingObjectPtr pCurve = 0;
                                    if (m_aux_scratch) {
                                        pCurve = ksTransferReference(m_aux_scratch, 0);
                                        if (pCurve) pContour->CopyCurve(pCurve, FALSE);
                                        m_aux_scratch = 0;
                                    }
                                    pCurve = obj? ksTransferReference(obj, 0) : 0;
                                    if (pCurve) pContour->CopyCurve(pCurve, FALSE);
                                    if (obj) DeleteObj(obj);
                                }
                            }
                            equi_curves.push_back(curve);
                            equi_curves_count++;
                        }
                    }

                    if (edge_index != 0) DrawnEdges.insert(KeyValPair(edge_index, prev_index));
                    if (PseudoGroupMode == true && edge_index > 0)
                    {
/*                        m_PseudoGroup.push_back(LineEnds[edge_index - 1].curve);
                        m_PseudoGroupStartPoints.push_back(std::pair<double,double>(x1,y1));*/
                    }
                    locContoursPool.erase(prev_index);
                }

            } while (node_index != 0 && node_index != node_index0);

            if (need_break) break;
            locContoursPool.erase(node_index);
            if (PseudoGroupMode == false) {
                if (contour_state == true)
                {
                //m_pPainter->ClosePath();
                //m_pPainter->Stroke();
                    m_aux_scratch = 0;
                    if (count_mode == false)
                    {
//Message("000");
                        if (m_issingle && m_swapped == false) m_BegPoint.x = beg_x2, m_BegPoint.y = beg_y2;
                        //CheckTipWithScratch(beg_x1, beg_y1);
                        if (m_swapped == false)
                            CheckSwapBegPoint(beg_x1, beg_y1, beg_x2, beg_y2);
                        else
                            CheckSwapBegPoint(beg_x2, beg_y2, beg_x1, beg_y1);
                        if (m_aux_scratch) {
                            if (idcContour == 0) {
                                //Contour(1);
                                //m_pKompasDoc5->ksContour(1);
                                idcContour = idcContours->Add();
                                idcContour->QueryInterface(&pContour);
                            }
                            IDrawingObjectPtr pCurve = 0;
                            if (m_aux_scratch) {
                                pCurve = ksTransferReference(m_aux_scratch, 0);
                                if (pCurve) pContour->CopyCurve(pCurve, FALSE);
                                m_aux_scratch = 0;
                            }
                            reference obj = 0;
                            if (m_issingle) {
                                m_BegPoint.x = (m_swapped == false)? beg_x1 : beg_x2;   // NO MORE scratches!
                                m_BegPoint.y = (m_swapped == false)? beg_y1 : beg_y2;
                                obj = Draw2DElement(singleton);    //, m_issingle);
                            }
                            if (obj) {
                                pCurve = obj? ksTransferReference(obj, 0) : 0;
                                if (pCurve) pContour->CopyCurve(pCurve, FALSE);
                                if (obj) DeleteObj(obj);
                                m_issingle = false;
                            }
                        }
                    }
/*
                    if (equal_points(m_BegPoint.x, m_BegPoint.y, beg_x1, beg_y1, m_precision * m_MmToUnits)
                        && equal_points(m_BegPoint.x, m_BegPoint.y, beg_x1, beg_y1) == false) {
                        reference aux_scratch = LineSeg(m_BegPoint.x, m_BegPoint.y, beg_x1, beg_y1, m_curves_style);
                        AddObjGroup(m_grpAuxScratches, aux_scratch);
                    }
*/
                    contour_state = false;
                    reference contour = 0;
                    //contour = (!issingle)? EndObj() : singleton;
                    //contour = (!m_issingle)? m_pKompasDoc5->ksEndObj() : singleton;

                    if (contours_count++, count_mode == false)
                    {
                        if (pContour) {
                            int pcount = pContour->Count;
                            BOOL Closed = pContour->Closed;
                            pContour->Closed = TRUE;
                            if (pContour->Count > 0)
                            {
                                idcContour->Update();
                                PumpWaitingMessages();
                            }
                        }
                        contour = m_issingle? singleton : idcContour->Reference;
                    }

                    equi_contour::pThis = this;

                    if (count_mode == false)
                        equi_contours.push_back(equi_contour(contour, m_issingle, m_swapped, equi_curves, equi_curves_count));
                    equi_curves.clear();

                    // moved for later!
                    //equiparam->side = CheckContourSide(contour, issingle);
                    //equiparam->geoObj = contour;
                    //Equidistant(equiparam); //параметры эквидистанты
                    //if (contour) DeleteObj(contour);

                    break;      // IMPORTANT! after each contour
                }
            } else {
/*                DrawProtoContour2(true);
                m_PseudoGroup.clear();
                m_PseudoGroupStartPoints.clear()*/
            }

/*            RefreshKompasActiveDocument();
            ksExecuteKompasCommand(ksCMRefresh, 1);
            kompasAPI->ksPumpWaitingMessages();*/
        }

        size_t EDGES = LineEnds.size();
        size_t VERTICES = locNodesPool.size();
        size_t COMPONENTS = comp_map.size();
        size_t CONTOURS = EDGES + COMPONENTS - VERTICES;

        if (count_mode) {
            //contours_count += CONTOURS; - may be counted as so too (but no interactivity in dialog)
            //break;      // no need in ii-cycle
        }
if(0)
MESSAGE(L"Map (line type %d, subcycle = %d) \n\nEDGES = %d\nVERTICES = %d\nCOMPONENTS = %d\n\nContours = %d" \
        MESSAGE_COMMA ii0 MESSAGE_COMMA ii MESSAGE_COMMA EDGES MESSAGE_COMMA VERTICES MESSAGE_COMMA COMPONENTS MESSAGE_COMMA CONTOURS)

        //if (ii == 0)
        {
            size_t LineEndsSize = LineEnds.size();
            for (size_t j = LineEndsSize; j > 0; j--)
            {
                size_t index = j - 1;
                size_t node1 = LineEnds[index].node_index1;
                size_t node2 = LineEnds[index].node_index2;
                map_Iter = DrawnEdges.find(index + 1);      // (ordered from 1!)
                if (map_Iter != DrawnEdges.end() && (map_Iter->second == node1 || map_Iter->second == node2))
                    LineEnds.erase(LineEnds.begin() + index);
            }
        }

#if defined(DO_DEBUG) == 0   //LICENCING!
        if (count_mode == false && contours_count >= 10) { contours_left = false; continue; break; }
#endif
        contours_left = (ii == 0) || (EDGES + COMPONENTS - VERTICES > 0);   // may be insufficient?
        contours_left = contours_left || LineEnds.size() > 0;
      }

if (0)
        if (equi_contours.size() > 0)     // drawing equidistants and clearing
        {
#if defined(DO_DEBUG)
MESSAGE(L"equi_contours.size() = %d" MESSAGE_COMMA equi_contours.size())
#endif
//MESSAGE(L"equi_contours.size() > 0")
            // WRONG!?:
            //std::sort(equi_contours.begin(), equi_contours.end(), equi_contour::SortedContours);

            std::sort(equi_contours.begin(), equi_contours.end(), equi_contour::SortedContours);

            int enclosed_count = 0;
            bool enclosed = false;
            reference enclosed_contour = 0;
            for (int i = (int)equi_contours.size() - 1; i >= 0; i--)
            {
                if (enclosed_contour)
                    enclosed = IsContourInsideContour(equi_contours[i].contour, enclosed_contour, this);
                if (!enclosed) {
                    if (enclosed_contour) DeleteObj(enclosed_contour);
                    enclosed_contour = 0;
                }
                if (enclosed)
                    enclosed_count += 1;
                else enclosed_count = 0;
                reference contour = equi_contours[i].contour;
                m_issingle = equi_contours[i].issingle;
                bool trigger_inside = (m_ProcessEnclosedContours != 0) && (enclosed_count % 2) != 0;
                bool right_ort = true;
                int inside = CheckContourSide(contour, equi_contours[i].swapped_curve, m_issingle);
                int side = (inside == 3)? (m_StockOutCheck != 0? 1 : 0) :       // left side or both!
                           (inside == 1)? (m_StockOutCheck != 0? 0 : 1) : 0;    // right side or both!
    char buf[128];
    ::sprintf(buf, "inside : %d, side : %d", inside, side);
//    ::Message(buf);
                if (!count_mode)
                {
                  if (equiparam->side < 2) {
                    equiparam->side = side;
                    if (trigger_inside) equiparam->side = equiparam->side == 1? 0 : 1;
                    if (equiparam->side == 0) {
                        equiparam->radLeft = std::max(0.0, ((inside == 3) == trigger_inside)? m_StockOut : m_StockIn);
                        equiparam->radLeft *= m_MmToUnits;
                    } else {
                        equiparam->radRight = std::max(0.0, ((inside == 3) == trigger_inside)? m_StockIn : m_StockOut);
                        equiparam->radRight *= m_MmToUnits;
                    }
                  } else {
                    if ((side == 0) == trigger_inside) {
                        equiparam->radRight = std::max(0.0, m_StockOut); // радиус эквидистанты справа по направлению кривой
                        equiparam->radLeft = std::max(0.0, m_StockIn);  // радиус эквидистанты слева
                    } else {
                        equiparam->radRight = std::max(0.0, m_StockIn); // радиус эквидистанты справа по направлению кривой
                        equiparam->radLeft = std::max(0.0, m_StockOut); // радиус эквидистанты слева
                    }
                    equiparam->radRight *= m_MmToUnits;
                    equiparam->radLeft *= m_MmToUnits;
                  }
                  equiparam->geoObj = contour;
                }
                equi_curves.clear();
                if (contours_count++, count_mode == false)
                {
                    equi_curves.insert(equi_curves.begin(),equi_contours[i].equi_curves.begin(),equi_contours[i].equi_curves.end());
                    equi_contours[i].equi_curves.clear();
                    if (TestInterrupt("Прервать операцию?", contours_count)) return contours_count;

                    /*if (Equidistant(equiparam) == 0) //параметры эквидистанты
                        Message("Error drawing Equidistant");*/
                    equi_curves_count = equi_contours[i].equi_curves_count;
                    if (DrawEquidistant(equiparam, equi_contours[i].swapped_curve) == 0) //параметры эквидистанты
                        Message("Error drawing Equidistant");
                    for (int ic = 0; ic < (int)equi_curves.size(); ic++) {
                        if (ic == 0 && m_CurveParts.count(equi_curves[0]) > 0) {
                            ProcessMarks(m_CurveParts.find( equi_curves[0] )->second, equi_contours[i].contour, equiparam);
                            break;
                        }
                        ProcessMarks(equi_curves[ic], equi_contours[i].contour, equiparam);
                    }
                    equi_curves.clear();
                }
//    char buf[128];
//    ::sprintf(buf, "контур : %d", i);
//    ::Message(buf);
                if (!enclosed_contour) enclosed_contour = equi_contours[i].contour;
                if (contour && enclosed_contour != contour) DeleteObj(contour);
                //if (enclosed)
                //    enclosed_count += 1;
                //else enclosed_count = 0;
            }
            if (enclosed_contour) DeleteObj(enclosed_contour);
            equi_contours.clear();
        }

if (1)  // SLOW ENCLOSING VERSION!!
        if (equi_contours.size() > 0)     // drawing equidistants and clearing
        {
#if defined(DO_DEBUG)
MESSAGE(L"SLOW equi_contours.size() > 0 \n equi_contours.size() = %d" MESSAGE_COMMA equi_contours.size())
#endif
            // WRONG!?:
            //std::sort(equi_contours.begin(), equi_contours.end(), equi_contour::SortedContours);

            for (int i = 0; i < (int)equi_contours.size(); i++)
                for (int j = 0; j < i; j++)
            {
                bool enclosed = IsContourInsideContour(equi_contours[j].contour, equi_contours[i].contour, this);
                if (enclosed) equi_contours[j].enclose_count++;
                else if (IsContourInsideContour(equi_contours[i].contour, equi_contours[j].contour, this))
                    equi_contours[i].enclose_count++;
            }

            //std::sort(equi_contours.begin(), equi_contours.end(), equi_contour::SortedContours);

            if (count_mode) contours_count += equi_contours.size();
            else
#if defined(DO_DEBUG)   //LICENCING!
            for (int i = (int)equi_contours.size() - 1; i >= 0; i--)
#else                   //LICENCING!
                for (int i = std::min((int)(equi_contours.size() - 1), 10 - 1); i >= 0; i--)
#endif
            {
                int enclosed_count = equi_contours[i].enclose_count;
                reference contour = equi_contours[i].contour;
                m_issingle = equi_contours[i].issingle;
                bool trigger_inside = (m_ProcessEnclosedContours != 0) && (enclosed_count % 2) != 0;
                bool right_ort = true;
                EquidistantParam loc_equiparam = *equiparam;
#if defined(DO_DEBUG)
//::Message("Prepare equiparam");
#endif
                int inside = CheckContourSide(contour, equi_contours[i].swapped_curve, m_issingle);
#if defined(DO_DEBUG)
//if (inside == 1)::Message("Prepare equiparam = inside == 1!");
//if (inside == 3)::Message("Prepare equiparam = inside == 3!");
//if (inside == 2)::Message("Prepare equiparam = inside == 2!");
//if (inside == 0)::Message("Prepare equiparam = inside == 0!");
#endif
                int side = (inside == 1)? (m_StockOutCheck != 0? 1 : 0) :       // left side or both!
                           (inside == 3)? (m_StockOutCheck != 0? 0 : 1) : 0;    // right side or both!
                if (loc_equiparam.side < 2) {
                    loc_equiparam.side = side;
                    if (trigger_inside) loc_equiparam.side = loc_equiparam.side == 1? 0 : 1;
                    if (loc_equiparam.side == 0) {
                        loc_equiparam.radLeft = std::max(0.0, ((inside == 1) == trigger_inside)? m_StockOut : m_StockIn);
                        loc_equiparam.radLeft *= m_MmToUnits;
                    } else {
                        loc_equiparam.radRight = std::max(0.0, ((inside == 1) == trigger_inside)? m_StockIn : m_StockOut);
                        loc_equiparam.radRight *= m_MmToUnits;
                    }
                } else {
                    if ((side == 0) == trigger_inside) {
                        loc_equiparam.radRight = std::max(0.0, m_StockOut); // радиус эквидистанты справа по направлению кривой
                        loc_equiparam.radLeft = std::max(0.0, m_StockIn);  // радиус эквидистанты слева
                    } else {
                        loc_equiparam.radRight = std::max(0.0, m_StockIn); // радиус эквидистанты справа по направлению кривой
                        loc_equiparam.radLeft = std::max(0.0, m_StockOut); // радиус эквидистанты слева
                    }
                    loc_equiparam.radRight *= m_MmToUnits;
                    loc_equiparam.radLeft *= m_MmToUnits;
                }
                loc_equiparam.geoObj = contour;

                if (TestInterrupt("Прервать операцию?", contours_count)) return contours_count;

                if (equi_contours[i].swapped_curve) {
#if defined(DO_DEBUG)
//::Message("Prepare equiparam = SWAPPED!");
#endif
                    if (loc_equiparam.side != 2) loc_equiparam.side = loc_equiparam.side == 0? 1 : 0;
                    double tmp = loc_equiparam.radLeft;
                    loc_equiparam.radLeft = loc_equiparam.radRight;
                    loc_equiparam.radRight = tmp;
                }
                equi_curves.clear();
                equi_curves.insert(equi_curves.begin(),equi_contours[i].equi_curves.begin(),equi_contours[i].equi_curves.end());
                equi_contours[i].equi_curves.clear();
                /*if (Equidistant(&loc_equiparam) == 0) //параметры эквидистанты
                    Message("Error drawing Equidistant");*/
                equi_curves_count = equi_contours[i].equi_curves_count;
                IDrawingContourPtr idcContour = 0;
                IDrawingObjectPtr idoObject = 0;
                if (m_issingle) idoObject = ksTransferReference(contour, 0);
                else idcContour = ksTransferReference(contour, 0);
                if (m_issingle == false) idoObject = idcContour;
                if (DrawEquidistant7(idoObject, &loc_equiparam, equi_contours[i].swapped_curve) == 0) //параметры эквидистанты
                    Message("Error drawing Equidistant");
                for (int ic = 0; ic < (int)equi_curves.size(); ic++) {
                    if (ic == 0 && m_CurveParts.count(equi_curves[0]) > 0) {
                        ProcessMarks(m_CurveParts.find( equi_curves[0] )->second, equi_contours[i].contour, &loc_equiparam);
                        break;
                    }
                    ProcessMarks(equi_curves[ic], equi_contours[i].contour, &loc_equiparam);
                }
                equi_curves.clear();
//    char buf[128];
//    ::sprintf(buf, "контур : %d", i);
//    ::Message(buf);
                //if (contour) DeleteObj(contour);
                if (idcContour) idcContour->Delete();
                //if (idoObject) idoObject->Delete();       // NEVER!
            }
            equi_contours.clear();
        }



        if (m_grpAuxScratches && ExistGroupObj(m_grpAuxScratches))
            DeleteObj(m_grpAuxScratches);
        m_grpAuxScratches = 0;
    }

//    if (rGroup) DeleteObj(rGroup);
//    if (rGroup) DeleteObj(rGroup);
    return contours_count;
}

void CSewingDlg::ProcessMarks(reference curve, reference contour, EquidistantParam* equiparam)
{
    if (m_MarkingCheck == 0 || !curve || !equiparam || m_MaxMarksNum < 1) return;
#if defined(DO_DEBUG)
//Message("ProcessMarks");
#endif

    if (contour == 0) contour = curve;

    const double perimeter =  ksGetCurvePerimeter (curve, ST_MIX_MM);
    bool closed = ksIsCurveClosed (curve) == 1;
//    if (perimeter < m_MaxMarksNum * m_MarkingStep) return;
    if (perimeter < m_MarkingStep) return;

    int type = GetObjParam(curve, 0, 0, ALLPARAM);
    double t1, t2, x1, y1, x2, y2;
    ksGetCurveMinMaxParametr (curve, &t1, &t2);
    ksGetCurvePoint(curve, t1, &x1, &y1);
    ksGetCurvePoint(curve, t2, &x2, &y2);

ksGetCurvePoint(curve, t1*0.8+t2*0.2, &x1, &y1);
Point (x1, y1, 5);

    reference pointArr = ksPointsOnCurve (curve, m_MaxMarksNum + (closed? 0 : 1+1));
    if (!pointArr) return;

    for (int i = (closed? 0 : 1); i < m_MaxMarksNum + (closed? 0 : 1); i++)
    {
        //double t = t1 + i * ((t2 - t1) / m_MaxMarksNum);      // doesn't give equal steps!
        MathPointParam par;
        if (GetArrayItem (pointArr, i, &par, sizeof (MathPointParam)))
        {
            double kx = par.x, ky = par.y, norm;
            //ksGetCurvePoint(curve, t, &kx, &ky);
            double kx0 = kx, ky0 = ky;
            norm = ksGetCurvePerpendicular(curve, kx, ky);

            int type = GetObjParam(curve, 0, 0, ALLPARAM);
            double nx = kx0, ny = ky0;
            MovePoint(&nx, &ny, norm, 0.1);
            double tx = kx0, ty = ky0;
            ksGetCurvePointProjection(contour, tx, ty, &tx, &ty);
            double tx0 = tx, ty0 = ty;
            bool right_ort = true;
            int dir = 1;
            if (ksMovePointOnCurve (contour, &tx, &ty, 0.01, dir))
            {
                right_ort = 0 <= ( (tx - tx0) * (ny - ky0) - (nx - kx0) * (ty - ty0) );
            }

            if (equiparam->side%2 == 0) {   // 0 or 2
                MovePoint(&kx, &ky, norm + (right_ort? 0 : 180), equiparam->radLeft);
                LineSeg(kx0, ky0, kx, ky, EQUI_STYLE);
            }
            if (equiparam->side >= 1) {     // 1 or 2
                kx = kx0, ky = ky0;
                MovePoint(&kx, &ky, norm + (right_ort? 180 : 0), equiparam->radRight);
                LineSeg(kx0, ky0, kx, ky, EQUI_STYLE);
            }
        }
    }
    ::ClearArray(pointArr);
    ::DeleteArray(pointArr);
}

int CSewingDlg::CheckContourSide(reference contour, bool swapped, bool issingle)
{
//    if (m_StockOutCheck != 0 && m_StockInCheck != 0) return 2;
    if (contour)
    {
        int type = GetObjParam(contour, 0, 0, ALLPARAM);
        double t1, t2, x1, y1, x2, y2;
        double perimeter =  ksGetCurvePerimeter (contour, ST_MIX_MM);
        ksGetCurveMinMaxParametr (contour, &t1, &t2);
        ksGetCurvePoint(contour, t1, &x1, &y1);
        ksGetCurvePoint(contour, t2, &x2, &y2);
if (ksIsCurveClosed(contour) == 0 && equal_points(x1, y1, x2, y2) == false)
Message("Equidistant: ERROR build contour - CURVE not closed!");

        reference tmp_ref = 0;
        if (issingle && type != CONTOUR_OBJ) {
            Contour(1);
            reference robj = Draw2DElement(contour, true);
            tmp_ref = EndObj();
            if (robj) DeleteObj(robj);
            if (tmp_ref) contour = tmp_ref;
        }

        const int idiv = 5;
        double step = (t2 - t1) / idiv;
        double t = t1;
        int inside = 0;
        for (int i = 0; i < idiv; i++, t += step)
        {
#if defined(DO_DEBUG)
//::Message("entering CheckIntersections");
#endif
            inside = CheckIntersections(contour, swapped, t);
char buf[128];
::sprintf(buf, "координаты %d-й точки : %d", i, inside);
//::Message(buf);
            if (inside == 3) break;
            if (inside == 1) break;
        }
        if (tmp_ref) DeleteObj(tmp_ref);
        //if (inside == 1) return (m_StockOutCheck != 0)? 1 : 0;  // left side or both!
        //if (inside == 3) return (m_StockOutCheck != 0)? 0 : 1;  // right side or both!
        return inside;
    }
    return 0;
}

int CSewingDlg::CheckIntersections(reference contour, bool swapped, double t)
{
    double kx, ky, norm;
    ksGetCurvePoint(contour, t, &kx, &ky);
    double kx0 = kx, ky0 = ky;
    norm = ksGetCurvePerpendicular(contour, kx, ky);
    int ret = 0;
    //MovePoint(&kx, &ky, norm, 1);
    reference norm_line = Line(kx, ky, norm);
    reference intersections = ::CreateArray(POINT_ARR, 0); // создать пустой массив точек пересечения
    if (::ksIntersectCurvCurv(norm_line, contour, intersections) > 0)
    {
        int count = ::GetArrayCount(intersections); // количество элементов в массиве
        MathPointParam par;
        double dist = -1.0;
        for (int i = 0; i < count; i++)
        {
            if (!::GetArrayItem(intersections, i, &par, sizeof(MathPointParam))) continue;
            if (!equal_points(kx0, ky0, par.x, par.y)) {
                double test_dist = DistancePntPnt(kx0, ky0, par.x, par.y);
                if (dist <= 0.0) kx = par.x, ky = par.y, dist = test_dist;
                else if (test_dist < dist) kx = par.x, ky = par.y, dist = test_dist;
            }
        }
        const double kxmid = (kx + kx0) / 2, kymid = (ky + ky0) / 2;
        double ort = 0.1, kxort = kxmid, kyort = kymid;
        if (dist > 0.0) ::ksGetCurvePointProjection (contour, kxort, kyort, &kxort, &kyort);
        int type = GetObjParam(contour, 0, 0, ALLPARAM);
        int side = 0;
        if (dist <= 0.0) side = 2;
        else if (DistancePntPnt(kxmid, kymid, kxort, kyort) < ort * m_MmToUnits) side = 2;
        else if (type != RECTANGLE_OBJ && type != REGULARPOLYGON_OBJ)
            side = ksIsPointInsideContour(contour, kxmid, kymid, 1e-6);
        else {  // special cases
            if (type == RECTANGLE_OBJ) {
                RectangleParam rpRectangleParam;
                if (GetObjParam(contour, &rpRectangleParam, sizeof(RectangleParam), ALLPARAM))
                {
                    double x = rpRectangleParam.x,
                           y = rpRectangleParam.y;
                    double angle = rpRectangleParam.ang;
                    double w = rpRectangleParam.width,
                           h = rpRectangleParam.height;
                    side = 3;
                }
            }
            if (type == REGULARPOLYGON_OBJ) {
                RegularPolygonParam rppRegularPolygonParam;
                if (GetObjParam(contour, &rppRegularPolygonParam, sizeof(RegularPolygonParam), ALLPARAM))
                {
                    double xc = rppRegularPolygonParam.xc,
                           yc = rppRegularPolygonParam.yc;
                    double angle = rppRegularPolygonParam.ang;
                    double r = rppRegularPolygonParam.radius;
                    int sides = rppRegularPolygonParam.count;
                    int inscribed = rppRegularPolygonParam.describe;

                    r = (inscribed == 0)? r/2 : r;
                    side = (DistancePntPnt(xc, yc, kxmid, kymid) < r)? 3 : 1;
                }
            }
        }
/*        MovePoint(&kx0, &ky0, norm, dist);
        if (equal_points(kx, ky, kx0, ky0)) ret = side;
        else ret = (side == 1)? 3 : (side == 3)? 1 : side;*/
        double tx = kx0, ty = ky0;
        int dir = swapped? -1 : 1;
        if (ksMovePointOnCurve (contour, &tx, &ty, 0.01, dir))
        {
            bool right_ort = 0 <= ( (kx - kx0) * (ty - ky0) - (tx - kx0) * (ky - ky0) );
#if defined(DO_DEBUG)
//if (right_ort)
//::Message("CheckIntersections : right_ort == true");
//else
//::Message("CheckIntersections : right_ort == false");
#endif
            if (!right_ort) side = (side == 1)? 3 : (side == 3)? 1 : side;
            //right = right_ort;
        }
        ret = side;
    }
    ::ClearArray(intersections);
    ::DeleteArray(intersections);
    if (norm_line) DeleteObj(norm_line);
    return ret;
}

reference CSewingDlg::DestroyContoursGroup(int tipSearch)
{
    ksDocument2DPtr m_pKompasDoc5 = kompasAPI->ActiveDocument2D();  // Current Kompas document       (for KAPI5 usage)

    IKompasDocumentPtr doc7 = newKompasAPI->ActiveDocument; // ksTransferReference( m_pKompasDoc5->reference, 0 );
    if (!doc7) { Message("No document open - destroy"); return 0; }

//return 0;
    std::vector<reference> locContours;    // destroyed from CONTOUR_OBJ
    do {
        KompasIteratorHolder contour_iterator(CreateIterator(tipSearch, 0));

        reference cur_obj_reference = MoveIterator(contour_iterator, 'F');
        while (cur_obj_reference)
        {
            int type = GetObjParam(cur_obj_reference, 0, 0, ALLPARAM);
            if (type != 0 && IsGeomObject( cur_obj_reference ) && (type == CONTOUR_OBJ))     // ONLY for CONTOURs!
            {
                //if (!ksIsCurveClosed(cur_obj_reference))    // !only not closed (skip singletons)
                    locContours.push_back(cur_obj_reference);
            }
            cur_obj_reference = MoveIterator(contour_iterator, 'N');
        }
    } while (0);

    if (locContours.size() == 0) return 0;

    reference a_group = 0, n_group = 0;

    // Creating doc for building of nurbs approximation
    DocumentParamT parDocument;
    memset( &parDocument, 0, sizeof( parDocument ) );
    parDocument.regim = 1;  // hidden mode
    parDocument.type = 3;
    reference tmp_doc = CreateDocumentT( &parDocument ); // fragment creation

    IKompasDocumentPtr tmp_doc7 = newKompasAPI->ActiveDocument; // ksTransferReference( m_pKompasDoc5->reference, 0 );
    doc7->Application->ActiveDocument = doc7;

    n_group = NewGroup(0);
    EndGroup();
    a_group = NewGroup(1);
    EndGroup();
    reference n_group0 = 0;

    for (int i = 0; i < (int)locContours.size(); i++)
    {
        //locContours[i] = ksCopyObj(locContours[i],0,0,0,0,1,0);
        AddObjGroup(a_group, locContours[i]);
//int iii = ExistGroupObj(a_group);

        if (a_group && ExistGroupObj(a_group))
        {
            doc7->Application->ActiveDocument = tmp_doc7;

            reference b_group = CopyGroupToDocument(a_group, m_pKompasDoc5->reference, tmp_doc);

            StoreTmpGroup( b_group );
            ksDestroyObjects(b_group);
            if (b_group) DeleteObj(b_group);

            reference c_group = NewGroup(1);
            EndGroup();
            SelectGroup(c_group, 2, 0,0,0,0);

            if (ExistGroupObj(c_group))
            {
                n_group0 = CopyGroupToDocument(c_group, tmp_doc, m_pKompasDoc5->reference);
                //ClearGroup(c_group);  - ! WRONG : this clears the group and DeleteObj(c_group) DOESN'T delete any objects!
            }
            if (c_group) DeleteObj(c_group);

            //doc7->Active = TRUE;
            doc7->Application->ActiveDocument = doc7;

            if (!n_group0) continue;

            StoreTmpGroup( n_group0 );

            do {
                KompasIteratorHolder contour_iterator(CreateIterator(ALL_OBJ, n_group0));

                reference cur_obj = MoveIterator(contour_iterator, 'F');
                while (cur_obj)
                {
                    int type = GetObjParam(cur_obj, 0, 0, ALLPARAM);
                    m_CurveParts.insert(std::pair <reference, reference> (cur_obj, locContours[i]));
                    AddObjGroup(n_group, cur_obj);
                    cur_obj = MoveIterator(contour_iterator, 'N');
                }
            } while (0);

            ClearGroup(n_group0);
        }
        ClearGroup(a_group);
    }
    if (tmp_doc) CloseDocument(tmp_doc);
    DeleteObj(a_group);
    if (n_group0) DeleteObj(n_group0);

    if (!ExistGroupObj(n_group)) { DeleteObj(n_group); n_group = 0; }

    return n_group;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// обработчики сообщений CSewingDlg
void CSewingDlg::api7test()
{
    UpdateData(TRUE);

    if (m_StockOutCheck == 0 && m_StockInCheck == 0) return;

    sewing_object->ShowWindow(SW_HIDE);
    EnableTaskAccess( 0 );          // Disable Kompas access
    ksUndoContainer(true);

    EquidistantParam equidParam;
    memset(&equidParam, 0, sizeof(equidParam));

    equidParam.side = m_StockOutCheck != 0 && m_StockInCheck != 0?
        2 : m_StockOutCheck != 0? 1 : 0;    // признак, с какой стороны строить эквидистанту
    equidParam.cutMode = 0;//1;  // тип обхода углов контура: 0-обход срезом, 1- обход дугой
    equidParam.degState = 0; // флаг разрешения вырожденных сегментов эквидистанты:
                             // 0-вырожденные сегменты запрещены, 1-вырожденные сегменты разрешены

    equidParam.radRight = std::max(0.0, m_StockOut); // радиус эквидистанты справа по направлению кривой
    equidParam.radLeft = std::max(0.0, m_StockIn); // радиус эквидистанты слева

    equidParam.radRight *= m_MmToUnits;
    equidParam.radLeft *= m_MmToUnits;

    equidParam.style = EQUI_STYLE;  // 2; // тип линии

    int tipSearch = SELECT_GROUP_OBJ;   // : ALL_OBJ;

    KompasIteratorHolder group_iterator(CreateIterator(tipSearch, 0));

    reference cur_obj_reference = MoveIterator(group_iterator, 'F');
    if (cur_obj_reference == 0) Message("Nothing selected");
    else
    {
/*        while (cur_obj_reference)
        {
            equi_curves.clear();
            int type = GetObjParam(cur_obj_reference, 0, 0, ALLPARAM);
            if (type != 0 && IsGeomObject( cur_obj_reference ))
            {
                equi_curves_count = 1;
                equi_curves.push_back(cur_obj_reference);
                equidParam.geoObj = cur_obj_reference;
                DrawEquidistant(&equidParam, false);
                equi_curves.clear();
            }
            cur_obj_reference = MoveIterator(group_iterator, 'N');
        }*/

        equi_curves.clear();
        equi_curves_count = 0;
        while (cur_obj_reference)
        {
            int type = GetObjParam(cur_obj_reference, 0, 0, ALLPARAM);
            if (type != 0 && IsGeomObject( cur_obj_reference ))
            {
                equi_curves.push_back(cur_obj_reference);
                equi_curves_count++;
            }
            cur_obj_reference = MoveIterator(group_iterator, 'N');
        }

        IKompasDocumentPtr doc7 = newKompasAPI->ActiveDocument; // ksTransferReference( m_pKompasDoc5->reference, 0 );
        IKompasDocument2DPtr pkdDocument = 0;
        doc7->QueryInterface(&pkdDocument);
        IViewPtr ivpView = pkdDocument->ViewsAndLayersManager->Views->View[0];
        IDrawingContainerPtr pdcContainer = 0;
        ivpView->QueryInterface(&pdcContainer);

        IDrawingContoursPtr idcContours = pdcContainer->DrawingContours;
        IDrawingContourPtr idcContour = 0;
        IContourPtr pContour = 0;

        idcContour = idcContours->Add();
        idcContour->QueryInterface(&pContour);

        IMath2DPtr mathp = newKompasAPI->Application->Math2D;

        int contour_count = pContour->Count;

        //ksDocument2DPtr m_pKompasDoc5  = kompasAPI->ActiveDocument2D();

        double t1, t2;
        double x1, y1, x2, y2;
        
        ksGetCurveMinMaxParametr (equi_curves[0], &t1, &t2);
        ksGetCurvePoint(equi_curves[0], t1, &x1, &y1);
        ksGetCurvePoint(equi_curves[0], t2, &x2, &y2);
        m_BegPoint.x = x1, m_BegPoint.y = y1;
        m_swapped = CheckSwapCurve(equi_curves[0]);
        if (m_swapped) m_BegPoint.x = x2, m_BegPoint.y = y2;
        //m_pKompasDoc5->ksContour(1);
        for (int i = 0; i < equi_curves.size(); i++)
        {
            //Contour(1);
            reference obj = 0;
            reference curve = equi_curves[i];
            //obj = Draw2DElement(equi_curves[i]);

            int type = GetObjParam(curve, 0, 0, ALLPARAM);
            if (type != 0 && IsGeomObject( curve ))
            {
//                IContourSegmentPtr icsSegment = 0;
                //ICurve2DPtr temp_curve = 0;
Message("case ELLIPSE_ARC_OBJ:");
                reference obj = Draw2DElement(curve);
                IDrawingObjectPtr pCurve = obj? ksTransferReference(obj, 0) : 0;
                if (pCurve) pContour->CopyCurve(pCurve, FALSE);
                contour_count = pContour->Count;
                IContourSegmentPtr icsSegment = pContour->Segment[0];

                if (obj) DeleteObj(obj);
            }
        }

        if (contour_count > 0)
        {
            idcContour->Update();
            PumpWaitingMessages();
        }


        //reference ref = m_pKompasDoc5->ksEndObj();
        reference ref = idcContour->Reference;
        if (ref == 0)  ref = pContour->Reference;

        /*equidParam.geoObj = ref;

        DrawEquidistant(&equidParam, m_swapped);
        if (ref) DeleteObj(ref);*/

        //if (ref == 0)
        {
            IEquidistantsPtr Equidistant_pool = pdcContainer->Equidistants;
            IEquidistantPtr Equidistant_curve = Equidistant_pool->Add();

            IDrawingObjectPtr drawing_object = idcContour;

            BOOL isok = false;
            BOOL test_isok = false;
            do {
                Equidistant_curve->BaseObject = drawing_object;
                Equidistant_curve->CutMode = equidParam.cutMode == 0? FALSE : TRUE;
                Equidistant_curve->DegenerateSegment = equidParam.degState == 0? FALSE : TRUE;
                Equidistant_curve->LeftRadius = equidParam.radLeft;
                Equidistant_curve->RightRadius = equidParam.radRight;
                Equidistant_curve->Side = equidParam.side == 2? ksETBoth :
                    equidParam.side == 0? ksETLeft : equidParam.side == 1? ksETRight : ksETUnknown;
                Equidistant_curve->Style = equidParam.style;

                isok = Equidistant_curve->Update();
            //if (!isok)
            // for (int i = 0; i < 10; i++) { PumpWaitingMessages(); isok = Equidistant_curve->Update(); if (isok) break; }
        //Message("Bad Equi->Update()");

            //pdoDrawingObject->LayerNumber = pdoDrawingObject->LayerNumber;
            //if (!pdoDrawingObject->Update()) Message("NO update");

                if (!isok) {
                    Equidistant_curve->DegenerateSegment = TRUE;
                    //Equidistant_curve->CutMode = TRUE;
                }
                isok = isok || test_isok;
                test_isok = true;
            } while (!isok);

            ref = Equidistant_curve->Reference;
        }

        if (idcContour) idcContour->Delete();

        equi_curves.clear();
        equi_curves_count = 0;
    }


    EnableTaskAccess( 1 );          // Enable Kompas access
    ksExecuteKompasCommand(ksCMRefresh, 1);
}


void CSewingDlg::OnBnClickedDrawOperation()
{
//return api7test();

    UpdateData(TRUE);

    if (m_StockOutCheck == 0 && m_StockInCheck == 0) return;

    sewing_object->ShowWindow(SW_HIDE);
    EnableTaskAccess( 0 );          // Disable Kompas access
    ksUndoContainer(true);

//  IKompasDocumentPtr m_pKompasDoc7;   // Current Kompas document    (for KAPI7/KAPI5 usage)
    ksDocument2DPtr m_pKompasDoc5 = kompasAPI->ActiveDocument2D();  // Current Kompas document       (for KAPI5 usage)

    m_CurveParts.clear();

    int tipSearch = (m_SelectType == enCurves_All)? ALL_OBJ_SHOW_ORDER :
        (m_SelectType == enCurves_Selected)? SELECT_GROUP_OBJ : ALL_OBJ;

    reference ex_group = CSewingDlg::DestroyContoursGroup(tipSearch);
    std::vector<reference> locApproxedGroups;

    if (m_SelectType == enCurves_Selected)
        AddObjGroup(0, ex_group);   // выделение!

    do {
        KompasIteratorHolder all_iterator(CreateIterator(tipSearch, 0));
        std::vector<reference> locApproxedCurves;

        reference cur_obj_reference = MoveIterator(all_iterator, 'F');
        while (cur_obj_reference)
        {
            int type = GetObjParam(cur_obj_reference, 0, 0, ALLPARAM);
            if (type != 0 && IsGeomObject( cur_obj_reference )
                && (type == CONTOUR_OBJ))//?? || type == BEZIER_OBJ))     // || type == NURBS_OBJ))
            {
                if (type != CONTOUR_OBJ)
                    locApproxedCurves.push_back(cur_obj_reference);
            }
            cur_obj_reference = MoveIterator(all_iterator, 'N');
        }

        for (int i = 0; i < (int)locApproxedCurves.size(); i++) {
            int type = GetObjParam(locApproxedCurves[i], 0, 0, ALLPARAM);
            reference tmp_grp = DrawApproximation(locApproxedCurves[i], type);
            if (tmp_grp && ExistGroupObj(tmp_grp))
            {
                locApproxedGroups.push_back( tmp_grp );
                do {
                    KompasIteratorHolder contour_iterator(CreateIterator(ALL_OBJ, tmp_grp));

                    reference cur_obj = MoveIterator(contour_iterator, 'F');
                    while (cur_obj)
                    {
                        int type = GetObjParam(cur_obj, 0, 0, ALLPARAM);
                        m_CurveParts.insert(std::pair <reference, reference> (cur_obj, locApproxedCurves[i]));
                        cur_obj = MoveIterator(contour_iterator, 'N');
                    }
                } while (0);

                if (m_SelectType == enCurves_Selected)
                    AddObjGroup(0, tmp_grp);   // выделение!
            }
        }
    } while (0);

    EquidistantParam equidParam;
    memset(&equidParam, 0, sizeof(equidParam));

//    equidParam.geoObj = obj;    //-базовая кривая эквидистанты

    equidParam.side = m_StockOutCheck != 0 && m_StockInCheck != 0?
        2 : m_StockOutCheck != 0? 1 : 0;    // признак, с какой стороны строить эквидистанту
    equidParam.cutMode = 0;//1;  // тип обхода углов контура: 0-обход срезом, 1- обход дугой
    equidParam.degState = 0; // флаг разрешения вырожденных сегментов эквидистанты:
                             // 0-вырожденные сегменты запрещены, 1-вырожденные сегменты разрешены

    equidParam.radRight = std::max(0.0, m_StockOut); // радиус эквидистанты справа по направлению кривой
    equidParam.radLeft = std::max(0.0, m_StockIn); // радиус эквидистанты слева

    equidParam.radRight *= m_MmToUnits;
    equidParam.radLeft *= m_MmToUnits;

    equidParam.style = EQUI_STYLE;  // 2; // тип линии

    KompasIteratorHolder group_iterator(CreateIterator(tipSearch, 0));

    PrepareContoursMakeEquidistants(group_iterator, &equidParam);

    for (int i = 0; i < (int)locApproxedGroups.size(); i++) DeleteObj(locApproxedGroups[i]);

    if (ex_group) {
        //if (ExistGroupObj(ex_group)) ClearGroup(ex_group);    // SKIP THIS to delete STORED group!
        DeleteObj(ex_group);
    }

    m_CurveParts.clear();

    ksUndoContainer(false);

    RefreshKompasActiveDocument();
    m_UndoRedo = 0;

    EnableTaskAccess( 1 );          // Enable Kompas access
    ((CButton*)GetDlgItem( IDC_UNDO ))->EnableWindow( ksIsKompasCommandEnable(ksCMEditUndo) != 0 );
    ((CButton*)GetDlgItem( IDC_REDO ))->EnableWindow( ksIsKompasCommandEnable(ksCMEditRedo) != 0 );
    sewing_object->ShowWindow(SW_NORMAL);
    sewing_object->SetActiveWindow();

    ksExecuteKompasCommand(ksCMRefresh, 1);
}

void CSewingDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    CDialog::OnKeyDown(nChar, nRepCnt, nFlags);
//    MessageBox(L"OnKeyDown");
}

extern void DrawRectNULL();
extern void DrawRectCallBack();

void CSewingDlg::OnBnClickedCancel()
{
    if (stateManualDrawMarks == FALSE)
        return OnCancel();

    stateManualDrawMarks = FALSE;
    ((CButton*)GetDlgItem( IDC_MANUAL_MARKS ))->SetWindowTextW( L"Ввод засечек" );

    ((CButton*)GetDlgItem( IDC_DATAEDIT ))->ShowWindow(SW_HIDE);

    ((CButton*)GetDlgItem( IDC_STATIC ))->ShowWindow(SW_SHOWNOACTIVATE);
    ((CButton*)GetDlgItem( IDC_STATIC2 ))->ShowWindow(SW_SHOWNOACTIVATE);
    ((CButton*)GetDlgItem( IDC_STATIC3 ))->ShowWindow(SW_SHOWNOACTIVATE);
    ((CButton*)GetDlgItem( IDC_STATIC4 ))->ShowWindow(SW_SHOWNOACTIVATE);
    ((CButton*)GetDlgItem( IDC_STATIC5 ))->ShowWindow(SW_SHOWNOACTIVATE);

    ((CButton*)GetDlgItem( IDC_STOCKOUT ))->ShowWindow(SW_SHOWNOACTIVATE);
    ((CButton*)GetDlgItem( IDC_STOCKIN ))->ShowWindow(SW_SHOWNOACTIVATE);
    ((CButton*)GetDlgItem( IDC_MARKING ))->ShowWindow(SW_SHOWNOACTIVATE);
    ((CButton*)GetDlgItem( IDC_MARKS_PER_CURVE ))->ShowWindow(SW_SHOWNOACTIVATE);
    ((CButton*)GetDlgItem( IDC_CURVE_MAX_POINTS ))->ShowWindow(SW_SHOWNOACTIVATE);
}

void CSewingDlg::OnBnClickedManualMarks()
{
    //bool res = sewing_object->ShowWindow(SW_HIDE) != FALSE; // Show dialog

    IDispatchPtr doc = kompasAPI->ksGetDocumentByReference(0);
    //if (1)
    if ( !BaseEvent::FindEvent( DIID_ksDocumentFileNotify, doc ) )
    {
        // Advising to Document etc. Events - DOESNT advise DocumentFrameEvent!
        AdviseDoc( kompasAPI, doc, kompasAPI->ksGetDocumentType( 0 ) );
    }
    //((CButton*)GetDlgItem( IDC_MANUAL_MARKS ))->EnableWindow( 0 );
    stateManualDrawMarks = stateManualDrawMarks == FALSE? TRUE : FALSE;
    if (stateManualDrawMarks == TRUE)
        ((CButton*)GetDlgItem( IDC_MANUAL_MARKS ))->SetWindowTextW( L"Сброс режима" );
    else
        ((CButton*)GetDlgItem( IDC_MANUAL_MARKS ))->SetWindowTextW( L"Ввод засечек" );

    ((CButton*)GetDlgItem( IDC_STATIC ))->ShowWindow(stateManualDrawMarks == FALSE? SW_SHOWNOACTIVATE : SW_HIDE);
    ((CButton*)GetDlgItem( IDC_STATIC2 ))->ShowWindow(stateManualDrawMarks == FALSE? SW_SHOWNOACTIVATE : SW_HIDE);
    ((CButton*)GetDlgItem( IDC_STATIC3 ))->ShowWindow(stateManualDrawMarks == FALSE? SW_SHOWNOACTIVATE : SW_HIDE);
    ((CButton*)GetDlgItem( IDC_STATIC4 ))->ShowWindow(stateManualDrawMarks == FALSE? SW_SHOWNOACTIVATE : SW_HIDE);
    ((CButton*)GetDlgItem( IDC_STATIC5 ))->ShowWindow(stateManualDrawMarks == FALSE? SW_SHOWNOACTIVATE : SW_HIDE);

    ((CButton*)GetDlgItem( IDC_STOCKOUT ))->ShowWindow(stateManualDrawMarks == FALSE? SW_SHOWNOACTIVATE : SW_HIDE);
    ((CButton*)GetDlgItem( IDC_STOCKIN ))->ShowWindow(stateManualDrawMarks == FALSE? SW_SHOWNOACTIVATE : SW_HIDE);
    ((CButton*)GetDlgItem( IDC_MARKING ))->ShowWindow(stateManualDrawMarks == FALSE? SW_SHOWNOACTIVATE : SW_HIDE);
    ((CButton*)GetDlgItem( IDC_MARKS_PER_CURVE ))->ShowWindow(stateManualDrawMarks == FALSE? SW_SHOWNOACTIVATE : SW_HIDE);
    ((CButton*)GetDlgItem( IDC_CURVE_MAX_POINTS ))->ShowWindow(stateManualDrawMarks == FALSE? SW_SHOWNOACTIVATE : SW_HIDE);

    ((CButton*)GetDlgItem( IDC_DATAEDIT ))->ShowWindow(stateManualDrawMarks == TRUE? SW_SHOWNOACTIVATE : SW_HIDE);

    //DrawRectNULL();
if (0)
    DrawRectCallBack();
if (0) {
//void CommandWindow_Example (void)
    RequestInfo info;
    memset(&info, 0, sizeof(info)); 
    info.commands = "!Окружность !Отрезок ";
    info.title = "Объекты";

    int j=CommandWindow(&info);
    switch (j) { 
    case 1: 
    Circle(10,10,10,1); 
    break; 
    case 2: 
    LineSeg(10,10, 20, 10, 1); 
    break; 
    }
}   // CommandWindow_Example
    //res = (sewing_object->ShowWindow(SW_NORMAL) != FALSE); // Show dialog
}

void CSewingDlg::OnBnClickedUndo()
{
    //bool res = sewing_object->ShowWindow(SW_HIDE) != FALSE; // Show dialog

    int cUndoRedo = ksCMEditUndo;   // m_UndoRedo == 0? ksCMEditUndo : ksCMEditRedo;
    //m_UndoRedo = m_UndoRedo == 0? 1 : 0;
    m_UndoRedo += 1;

    ksExecuteKompasCommand(cUndoRedo, 0);
    RefreshKompasActiveDocument();
    ((CButton*)GetDlgItem( IDC_UNDO ))->EnableWindow( ksIsKompasCommandEnable(ksCMEditUndo) != 0 );
    ((CButton*)GetDlgItem( IDC_REDO ))->EnableWindow( ksIsKompasCommandEnable(ksCMEditRedo) != 0 );
    ksExecuteKompasCommand(ksCMRefresh, 1);
}

void CSewingDlg::OnBnClickedRedo()
{
    int cUndoRedo = ksCMEditRedo;   // m_UndoRedo == 0? ksCMEditUndo : ksCMEditRedo;
    m_UndoRedo -= 1;

    ksExecuteKompasCommand(cUndoRedo, 0);
    RefreshKompasActiveDocument();
    ((CButton*)GetDlgItem( IDC_UNDO ))->EnableWindow( ksIsKompasCommandEnable(ksCMEditUndo) != 0 );
    ((CButton*)GetDlgItem( IDC_REDO ))->EnableWindow( ksIsKompasCommandEnable(ksCMEditRedo) != 0 );
    ksExecuteKompasCommand(ksCMRefresh, 1);
}

void CSewingDlg::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
    CDialog::OnActivate(nState, pWndOther, bMinimized);

    if (nState == WA_INACTIVE) return;

    int doctype = ksGetDocumentType(0);
    int dd1 = kompasAPI->ksGetDocumentType(0);
    IKompasDocumentPtr ppp = (IKompasDocumentPtr)kompasAPI->ksGetDocumentByReference(0);
    int ddd2 = ppp? ppp->DocumentType : 0;
    //if (doctype == 0) return;
    if (doctype == 0 ||
        (doctype != lt_DocSheetStandart && doctype != lt_DocSheetUser && doctype != lt_DocFragment))
    {
        bool actitest = ((CButton*)GetDlgItem( IDC_DRAW_OPERATION ))->IsWindowEnabled() != FALSE;
        ((CButton*)GetDlgItem( IDC_DRAW_OPERATION ))->EnableWindow( 0 );
        ((CButton*)GetDlgItem( IDC_UNDO ))->EnableWindow( 0 );
        ((CButton*)GetDlgItem( IDC_REDO ))->EnableWindow( 0 );
        ((CButton*)GetDlgItem( IDC_MANUAL_MARKS ))->EnableWindow( 0 );
        if (actitest)   // || doctype == 0)
            Message("Активный документ не является чертежом или фрагментом или не открыто никакого документа."
                   "\nПлагин будет отключен до перехода к поддерживаемому типу документа.");
    }
    else
    {
        ((CButton*)GetDlgItem( IDC_DRAW_OPERATION ))->EnableWindow( 1 );
        ((CButton*)GetDlgItem( IDC_UNDO ))->EnableWindow( ksIsKompasCommandEnable(ksCMEditUndo) != 0 );
        ((CButton*)GetDlgItem( IDC_REDO ))->EnableWindow( ksIsKompasCommandEnable(ksCMEditRedo) != 0 );
        ((CButton*)GetDlgItem( IDC_MANUAL_MARKS ))->EnableWindow( 1 );
            //((CEdit*)GetDlgItem( IDC_DATAEDIT ))->GetWindowTextLengthW() == 0? 1 : 0 );

        short unittype = 0;
        if (GetDocOptions(LENGTHUNITS_OPTIONS, &unittype, sizeof(unittype)))
            m_MmToUnits = (unittype == ST_MIX_MM)? 1.0 : (unittype == ST_MIX_SM)? 0.1 :
                          (unittype == ST_MIX_DM)? 0.01 : (unittype == ST_MIX_M)? 0.001 : 1.0;
        MmToUnits = m_MmToUnits;
    }
}


void CSewingDlg::ParseDoubleData(int nID)
{
    CEdit* ctrl_item = (CEdit*)sewing_object->GetDlgItem(nID);
    CString text;
    ctrl_item->GetWindowTextW(text);
    int chindex = text.Find('.');
    if (chindex >= 0) {
        chindex = text.Find('.', chindex + 1);
        if (chindex >= 0) ctrl_item->SetWindowTextW(text.Left(chindex));
    }
    if (ctrl_item->GetWindowTextLengthW() == 1 && text.Find('.') >= 0) ctrl_item->SetWindowTextW(_T(""));
    if (ctrl_item->GetWindowTextLengthW() == 0) ctrl_item->SetWindowTextW(_T("0"));
}

void CSewingDlg::OnEnKillfocusStockout()
{
    ParseDoubleData(IDC_STOCKOUT);
}

void CSewingDlg::OnEnKillfocusStockin()
{
    ParseDoubleData(IDC_STOCKIN);
}

void CSewingDlg::OnEnKillfocusMarking()
{
    ParseDoubleData(IDC_MARKING);
}

void CSewingDlg::OnEnKillfocusPrecision()
{
    ParseDoubleData(IDC_PRECISION);
}

void CSewingDlg::OnEnChangeMarking()
{
    // TODO:  Если это элемент управления RICHEDIT, то элемент управления не будет
    // send this notification unless you override the CDialog::OnInitDialog()
    // function and call CRichEditCtrl().SetEventMask()
    // with the ENM_CHANGE flag ORed into the mask.

    // TODO:  Добавьте код элемента управления
}
Третий Рим должен пасть!
Re: Оцените качество кода на С++
От: alxn1 Россия  
Дата: 18.09.14 09:58
Оценка: +5
На мой взгляд CSewingDlg — это обьект-бог, он делает столько всего, что аж дух захватывает. Накожено, может быть и ничего (стиль определенно чувствуется), но быстро разобраться в этом очень трудно, не говоря уже о поддержке такого. Это моя субьективная оценка

Здравствуйте, GhostCoders, Вы писали:

GC>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.
Re: Оцените качество кода на С++
От: niXman Ниоткуда https://github.com/niXman
Дата: 18.09.14 09:58
Оценка: +2
в общем, довольно не плохо с первого взгляда, видал и похуже %)
"порадовало" использование запятой:
if (basepoint == 0)
  Points.push_back(mpMathPoint.x),
  Points.push_back(mpMathPoint.y),
  Points.push_back(right2X),
  Points.push_back(right2Y);
else if (basepoint == basepoint_count - 1 && closed_path == false)
  Points.push_back(left2X),
  Points.push_back(left2Y),
  Points.push_back(mpMathPoint.x),
  Points.push_back(mpMathPoint.y);
else
  Points.push_back(left2X),
  Points.push_back(left2Y),
  Points.push_back(mpMathPoint.x),
  Points.push_back(mpMathPoint.y),
  Points.push_back(mpMathPoint.x),
  Points.push_back(mpMathPoint.y),   // doubling inner point!
  Points.push_back(right2X),
  Points.push_back(right2Y),
  Weights.push_back(1.0),
  Weights.push_back(1.0);   // doubling inner point!
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Отредактировано 18.09.2014 10:01 niXman . Предыдущая версия .
Re[2]: Оцените качество кода на С++
От: niXman Ниоткуда https://github.com/niXman
Дата: 18.09.14 09:59
Оценка:
и да, функции размером в двести строк, тоже доставляют %)
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[2]: Оцените качество кода на С++
От: uzhas Ниоткуда  
Дата: 18.09.14 10:14
Оценка: +1
Здравствуйте, alxn1, Вы писали:

A>Накожено, может быть и ничего (стиль определенно чувствуется)

стиля тут точно нет
члены класса:
    bool Activated;
    PointParam m_BegPoint;
    reference m_grpAuxScratches;
    unsigned short m_curves_style;


с остальным согласен, write-only код
Re: Оцените качество кода на С++
От: Kernighan СССР  
Дата: 18.09.14 10:15
Оценка: 5 (2) +5
Здравствуйте, GhostCoders, Вы писали:

GC>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.


Ну, этот код по читаемости гораздо лучше того, что пишу например я.
Чтобы разговор вышел из детсадовского уровня сформулируй свои претензии в виде слов — что тебе не нравится.
Я уверен, что твой сотрудник пойдёт тебе навстречу, если ты правильно сформулируешь.
Re: Оцените качество кода на С++
От: Кодт Россия  
Дата: 18.09.14 10:26
Оценка: -2
Здравствуйте, GhostCoders, Вы писали:

GC>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.


GC>
GC>// SewingDlg.h 

// мне казалось, что визард лепит стандартный инклуд-гард наряду с pragma once
// для MSVC его отсутствие, в общем-то, некритично, но всё же... культуру себе надо прививать

#ifndef __SEWING_DLG__H__
#define __SEWING_DLG__H__
// и в конце
#undef//__SEWING_DLG__H__

GC>#pragma once

GC>#include "MainHeaders.h" // почему не засунуто в "stdafx.h" ?
GC>#include "afxwin.h" // <afxwin.h> чтобы подчеркнуть, что этот хедер вне проекта, из системной библиотеки

GC>#include <vector>
GC>#include <map>

#define NOMINMAX // где-нибудь в самом начале stdafx.h

GC>#ifdef max
GC>#undef max
GC>#endif

GC>#ifdef min
GC>#undef min
GC>#endif


Вот эту часть я бы вынес в отдельный хедер. Явно же она нужна где-то ещё, и зачем в нагрузку к ней тащить диалог?
GC>class KompasObjectHolder // немец, что ли? CompassObjectHolder
GC>{
GC>protected:
GC>    reference m_reference; // не видя, как определён тип reference, сложно сказать, нужны ли все эти танцы с бубном...
GC>public:
GC>    KompasObjectHolder() : m_reference(0) {}
GC>    KompasObjectHolder(reference object_reference) : m_reference(object_reference) {}
GC>    KompasObjectHolder(KompasObjectHolder& prev_holder) : m_reference(prev_holder.m_reference) { prev_holder.m_reference = 0; }
GC>    KompasObjectHolder& operator =(KompasObjectHolder& prev_holder) {
GC>        if (!m_reference) m_reference = prev_holder.m_reference, prev_holder.m_reference = 0; return *this; }
GC>    ~KompasObjectHolder()
GC>    {
GC>        if (m_reference) DeleteObj(m_reference);
GC>        m_reference = 0;
GC>    }
GC>    operator reference() { return m_reference; }
GC>    operator bool() { return m_reference != 0; }
GC>};

// я бы сделал проще
typedef /*boost|std*/ shared_ptr<reference> compass_obj_ptr;
compass_obj_ptr make_compass_obj_ptr(reference r) { return compass_obj_ptr(r, DeleteObj); }


// вот это - кажется архитектурным злом. Наследовать один умный указатель от другого умного указателя...

GC>class KompasIteratorHolder : public KompasObjectHolder
GC>{
GC>public:
GC>    KompasIteratorHolder() {}
GC>    KompasIteratorHolder(reference iterator_reference) : KompasObjectHolder(iterator_reference) {}
GC>    KompasIteratorHolder& operator =(KompasIteratorHolder& prev_holder) {
GC>        if (!m_reference) m_reference = prev_holder.m_reference, prev_holder.m_reference = 0; return *this; }
GC>    ~KompasIteratorHolder()
GC>    {
GC>        if (m_reference) DeleteIterator(m_reference);
GC>        m_reference = 0;
GC>    }
GC>};

// быстрое-грязное решение (но опять же, я не знаю, что за сущность такая - reference)
compass_obj_ptr make_compass_obj_iter(reference r) { return compass_obj_ptr(r, DeleteIterator); }


В самом диалоге очень сильно настораживает обилие голых reference.
Надо уж определиться: или они подлежат какому-то контролю за временем жизни, или этот контроль не больно-то нужен.
Особенно это касается вектора и мапа. Ну хоть бы какое-то словесное примечание было: например, что время жизни диалога заведомо меньше времени жизни этих reference, переданных туда извне.
GC>class CSewingDlg : public CDialog       // диалоговое окно CSewingDlg
GC>{
GC>    DECLARE_DYNAMIC(CSewingDlg)

GC>public:
GC>    CSewingDlg(CWnd* pParent = NULL);   // стандартный конструктор
GC>    virtual ~CSewingDlg();

GC>    reference Draw2DElement(reference element, bool closed = false);
GC>    reference DrawLineSeg(reference element);
GC>    reference DrawCircle(reference element);
GC>    reference DrawArc(reference element);
GC>    reference DrawEllipse(reference element);
GC>        reference DrawEllipseArc(reference element);                    // BAD when coercing EllipseArc & Nurbs ??bag in KOMPAS
GC>    reference DrawEllipseArcWithNurbs(reference element);               // new API7+IMath2D version - !EllipseArc as case of Nurbs
GC>    reference DrawRectangle(reference element);
GC>    reference DrawRegularPolygon(reference element);
GC>        reference DrawBezier(reference element, bool closed = false);   // new API7 version - allows Bezier 2 type (Equidistants fail too)
GC>    reference DrawBezierWithNurbs(reference element, bool closed);      // new API7+IMath2D version - !Bezier as case of Nurbs
GC>    reference DrawEquidistant7(IDrawingObjectPtr& idcContour,
GC>                              EquidistantParam *equiparam, bool swapped);       // new API7 version - no adv.features
GC>    reference DrawEquidistant(EquidistantParam *equiparam, bool swapped);       // new API5 version - ?
GC>    reference DrawPolyline(reference element, bool closed = false);
GC>        reference DrawApproximation(reference element, int curve_type);
GC>    reference DrawContour(reference element, bool closed = false);
GC>    reference DrawNurbs(reference element, bool closed = false);    // new API7+IMath2D version - !works

GC>    reference SmallNurbs3(double x1, double y1, double x2, double y2, unsigned short style);

GC>    double CurvesDeviation(reference old_curve, ICurve2DPtr& new_curve, double precision, double* tpars, int points_count);

GC>    inline void KompasCorrectAngles(double& dBeginAngle, double& dEndAngle, bool clockwise);

GC>    int PseudoProcessAcceptedData(void* pInputData, EquidistantParam *equiparam, bool PseudoGroupMode);
GC>    void PrepareContoursMakeEquidistants(reference obj_iterator, EquidistantParam* equiparam);

GC>    void ProcessMarks(reference curve, reference contour, EquidistantParam* equiparam);

GC>    bool CheckSwapBegPoint(double& x1, double& y1, double& x2, double& y2);
GC>    bool CheckTipWithScratch(double& x1, double& y1);
GC>    bool CheckSwapCurve(reference curve);
GC>    reference DestroyContoursGroup(int tipSearch);
GC>    double GetEquidistantPerimeter(reference element);      // using destroyObj to get true length of Equidistant
GC>    int CheckContourSide(reference contour, bool swapped, bool issingle);
GC>    int CheckIntersections(reference contour, bool swapped, double t);

GC>    bool TestInterrupt(LPSTR msg, int count);

GC>void api7test();

GC>    bool Activated;
GC>    PointParam m_BegPoint;
GC>    reference m_grpAuxScratches;
GC>    unsigned short m_curves_style;

GC>// Данные диалогового окна
GC>    enum { IDD = IDD_SEWINGDLG };
GC>    enum { enCurves_All = 0, enCurves_Selected, enCurves_Unselected };

GC>protected:
GC>    virtual BOOL OnInitDialog();

GC>    void ParseDoubleData(int nID);

GC>    virtual void DoDataExchange(CDataExchange* pDX);    // поддержка DDX/DDV

GC>    DECLARE_MESSAGE_MAP()
GC>public:
GC>    afx_msg void OnBnClickedDrawOperation();
GC>    afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);
GC>    afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
GC>    afx_msg void OnBnClickedCancel();
GC>    afx_msg void OnBnClickedUndo();
GC>    afx_msg void OnBnClickedRedo();
GC>protected:
GC>    int m_StockOutCheck;
GC>    int m_StockInCheck;
GC>    int m_MarkingCheck;
GC>    double m_StockOut;
GC>    double m_StockIn;
GC>    double m_MarkingStep;
GC>    int m_SelectType;
GC>private:
GC>    int m_UndoRedo;
GC>    int m_ProcessClosedOnly;
GC>    int m_ContourHasAnyLineStyles;
GC>    int m_ProcessEnclosedContours;
GC>    int m_MaxSplitNum;
GC>    int m_MaxMarksNum;
GC>    double m_MmToUnits;

GC>    double m_precision;

GC>    std::map<reference, reference> m_CurveParts;
GC>    std::vector<reference> equi_curves;     // tmp vector needed to correct Equidistant behavior
GC>    int equi_curves_count;                  // may differ from equi_curves.size() due to m_grpAuxScratches
GC>    bool m_swapped;
GC>    bool m_issingle;
GC>    reference m_aux_scratch;
GC>    bool m_skip_scratch;        // in API5 Contour()-mode all references are 0 = i.e. temp intrinsic objects for Contour()
GC>public:
GC>    afx_msg void OnBnClickedManualMarks();
GC>    afx_msg void OnEnKillfocusPrecision();
GC>    afx_msg void OnEnKillfocusStockout();
GC>    afx_msg void OnEnKillfocusStockin();
GC>    afx_msg void OnEnKillfocusMarking();
GC>    afx_msg void OnEnChangeMarking();
GC>};

GC>


GC>
GC>// SewingDlg.cpp: файл реализации
GC>//

GC>#include "stdafx.h"
GC>#include "SewingDlg.h"
GC>#include "ProgressDlg.h"
GC>#include "SewingPlugin.h"

GC>#include "testEvents/BaseEvent.h"

GC>#include <limits>
GC>#include <cmath>
GC>#include <algorithm>

GC>const double PI2DEGREE = 180.0 / 3.14159265;
GC>const int EQUI_STYLE = ksCSConstruction;    // 6 : вспомогательная (красная)

GC>extern BOOL glb_Interrupt;

GC>//   from eventsAuto.cpp
GC>void AdviseDoc( KompasObject* kompas,
GC>                LPDISPATCH doc, long docType,
GC>                bool fSelectMng = true,
GC>                bool fObject = true,
GC>                bool fStamp = true,
GC>                bool fDocument = true,
GC>                bool fSpecification = true,
GC>                bool fSpcObject = true,
GC>                long objType = 0 /*= -1*/);


// это рукоделие или визард напихал?

GC>#if defined(ASSERT) && !defined(_DEBUG)
GC>#undef ASSERT
GC>#define FILEW2(x) L##x
GC>#define FILEW(f) FILEW2(f)
GC>#define ASSERT(f) {CString sss;if (!(f)){sss.Format(L"BAD ASSERT in line %d (file " FILEW(__FILE__) L")", __LINE__);MessageW((LPWSTR)sss.GetString());}}
GC>#endif

// ой-ой-ой, заплатки и борьба за имитацию вариадиков в макросах.

GC>#define MESSAGE_COMMA ,
GC>#define MESSAGE(mesh) {   CString sss; sss.Format(mesh); MessageW((LPWSTR)sss.GetString());   }


// inline лишний - тем более, внутри .cpp

GC>template<typename FloatType> inline // to improve need call_traits, etc
GC>bool approx_equals(FloatType x, FloatType y, FloatType relative_factor=FloatType(64))
GC>{
GC>    // default relative_factor=64 results in approximately 2 decimal digits tolerance in significand
GC>    using std::abs; using std::max; using std::numeric_limits;
GC>    return abs(x-y) <= ( relative_factor * numeric_limits<FloatType>::epsilon() * max(abs(x),abs(y)) );
GC>}

// static inline ? достаточно одного static или запихать в локальное пространство имён
// кстати, почему бы не вынести эти функции-утилитки в отдельный хедер?

GC>static inline bool equal_points(double x1, double y1, double x2, double y2, double precision = 0.0)
GC>{
GC>    // double epsilon = 1e-16, so 1e9 gives real Kompas precision about 1e-7..1e-6..1e-5
GC>    //return VTSL::Math::approx_equals<double>( x1, x2, double(1e9) ) && VTSL::Math::approx_equals<double>( y1, y2, double(1e9) );
GC>    using std::abs; using std::max; using std::numeric_limits;

GC>    double minerror = DBL_EPSILON * double(1e9);

GC>    if (precision > minerror)
GC>        return max(abs(x1 - x2), abs(y1 - y2)) <= precision;
GC>//Message("equal_points");

GC>//  double correction = double(1e9) * (precision <= minerror? 1.0 : precision / minerror);
GC>    double correction = (precision <= minerror? minerror : precision) / DBL_EPSILON;
GC>    bool bx, by;
GC>    double absmax = max(abs(x1), abs(x2));
GC>    if ( absmax > 1.0 ) bx = approx_equals<double>( x1, x2, correction );
GC>    else {
GC>        if ( absmax < minerror ) bx = true;
GC>            else bx = approx_equals<double>( x1/absmax, x2/absmax, correction );
GC>    }
GC>    absmax = max(abs(y1), abs(y2));
GC>    if ( absmax > 1.0 ) by = approx_equals<double>( y1, y2, correction );
GC>    else {
GC>        if ( absmax < minerror ) by = true;
GC>            else by = approx_equals<double>( y1/absmax, y2/absmax, correction );
GC>    }
GC>    return bx && by;
GC>}

GC>static inline bool equal_values(double val1, double val2,  double precision = 0.0)
GC>{
GC>    const double minerror = DBL_EPSILON * double(1e9);
GC>//  double correction = double(1e9) * (precision <= minerror? 1.0 : precision / minerror);
GC>    double correction = (precision <= minerror? minerror : precision) / DBL_EPSILON;
GC>    double absmax = std::max(abs(val1), abs(val2));
GC>    if ( absmax < 1.0 )
GC>    {
GC>        if (absmax < minerror) return true;    // DBL_EPSILON * 64
GC>        return approx_equals<double>( val1/absmax, val2/absmax, correction);
GC>    }
GC>    return approx_equals<double>( val1, val2, correction);
GC>}

GC>static bool IsContourInsideContour(reference contour, reference other, CSewingDlg* pThis)
GC>{
GC>    int type = GetObjParam(other, 0, 0, ALLPARAM);
GC>    int side = 0;
GC>    double t1, t2, x1, y1, x2, y2;
GC>    ksGetCurveMinMaxParametr (contour, &t1, &t2);
GC>    ksGetCurvePoint(contour, t1, &x1, &y1);
GC>    ksGetCurvePoint(contour, t2, &x2, &y2);

GC>    if (type == CONTOUR_OBJ) {
GC>        side = ksIsPointInsideContour(other, x1, y1, 1e-6);
GC>    }

GC>    if (side == 0) {    // special cases for single bezier contour etc.
GC>        reference bref = ksApproximationCurve(other, 0.1, 0, 0, 1);
GC>        if (bref)
GC>        {
GC>            int type = GetObjParam(bref, 0, 0, ALLPARAM);
GC>            if (type == CONTOUR_OBJ) {
GC>                side = ksIsPointInsideContour(bref, x1, y1, 1e-6);
GC>            }
GC>            DeleteObj(bref);
GC>        }
GC>    }


// if(...) do{ ... break ... }while(false) - оригинально , но как бы намекает, что это место следует отрефакторить


GC>    if (side == 0) do {    // special cases for single rectangle, circle etc.

GC>        KompasObjectHolder decomposed_group(DecomposeObj(other, 5, 0.5, 1));
GC>        if (!decomposed_group) break;

GC>        KompasIteratorHolder group_iterator(CreateIterator(ALL_OBJ, decomposed_group));
GC>        if (!group_iterator) break;

GC>        Contour(1);

GC>        reference cur_obj = MoveIterator(group_iterator, 'F');
GC>        while (cur_obj)
GC>        {
GC>            pThis->Draw2DElement(cur_obj);
GC>            cur_obj = MoveIterator(group_iterator, 'N');
GC>        }

GC>        reference contour_obj = EndObj();
GC>        if (contour_obj)
GC>            side = ksIsPointInsideContour(contour_obj, x1, y1, 1e-6);
GC>        if (contour_obj) DeleteObj(contour_obj);
GC>    } while (0);

GC>    if (side == 3) {
GC>Message("IsContourInsideContour");
GC>        int kp = 0;
GC>        IntersectCurvCurv (contour, other, &kp, 0, 0, 0);
GC>        if (kp == 0) return true;
GC>    }
GC>    return false;
GC>}

GC>// диалоговое окно CSewingDlg

GC>IMPLEMENT_DYNAMIC(CSewingDlg, CDialog)

GC>CSewingDlg::CSewingDlg(CWnd* pParent /*=NULL*/)
GC>    : CDialog(CSewingDlg::IDD, pParent)
GC>    , m_StockInCheck(false)
GC>    , m_StockOutCheck(false)
GC>    , m_MarkingCheck(false)
GC>    , m_StockOut(0)
GC>    , m_StockIn(0)
GC>    , m_MarkingStep(0)
GC>    , m_SelectType(0)
GC>    , m_UndoRedo(0)
GC>    , m_ProcessClosedOnly(1)
GC>    , m_ContourHasAnyLineStyles(0)
GC>    , m_ProcessEnclosedContours(0)      // enclosing may be too slow
GC>    , m_MaxSplitNum(20)
GC>    , m_MaxMarksNum(10)
GC>    , m_precision(1.0)
GC>{
GC>    Activated = true;
GC>    m_BegPoint.x = m_BegPoint.y = 0;
GC>    m_BegPoint.style = 0;
GC>    m_grpAuxScratches = 0; m_curves_style = 0;
GC>    m_MmToUnits = 1.0;
GC>    m_swapped = m_issingle = false;
GC>    m_aux_scratch = 0; m_skip_scratch = false;
GC>}

GC>CSewingDlg::~CSewingDlg()
GC>{
GC>}

GC>void CSewingDlg::DoDataExchange(CDataExchange* pDX)
GC>{
GC>    CDialog::DoDataExchange(pDX);
GC>    DDX_Radio(pDX,      IDC_RADIO1,             m_SelectType);
GC>    DDX_Check(pDX,      IDC_STOCKOUT_CHECK,     m_StockOutCheck);
GC>    DDX_Check(pDX,      IDC_STOCKIN_CHECK,      m_StockInCheck);
GC>    DDX_Check(pDX,      IDC_MARKING_CHECK,      m_MarkingCheck);
GC>    DDX_Check(pDX,      IDC_PROCESS_CLOSED_ONLY,            m_ProcessClosedOnly);
GC>    DDX_Check(pDX,      IDC_CONTOUR_HAS_ANY_LINE_STYLES,    m_ContourHasAnyLineStyles);
GC>    DDX_Check(pDX,      IDC_PROCESS_ENCLOSED_CONTUORS,      m_ProcessEnclosedContours);
GC>    DDX_Text (pDX,      IDC_STOCKOUT,           m_StockOut);
GC>    DDX_Text (pDX,      IDC_STOCKIN,            m_StockIn);
GC>    DDX_Text (pDX,      IDC_MARKING,            m_MarkingStep);
GC>    DDX_Text (pDX,      IDC_CURVE_MAX_POINTS,   m_MaxSplitNum);
GC>    DDX_Text (pDX,      IDC_MARKS_PER_CURVE,    m_MaxMarksNum);
GC>    DDX_Text (pDX,      IDC_PRECISION,          m_precision);
GC>}


GC>BEGIN_MESSAGE_MAP(CSewingDlg, CDialog)
GC>    ON_BN_CLICKED(IDC_DRAW_OPERATION, &CSewingDlg::OnBnClickedDrawOperation)
GC>//    ON_WM_ACTIVATE()
GC>    ON_WM_KEYDOWN()
GC>    ON_BN_CLICKED(IDCANCEL, &CSewingDlg::OnBnClickedCancel)
GC>    ON_BN_CLICKED(IDC_UNDO, &CSewingDlg::OnBnClickedUndo)
GC>    ON_BN_CLICKED(IDC_REDO, &CSewingDlg::OnBnClickedRedo)
GC>//    ON_WM_CHAR()
GC>ON_WM_ACTIVATE()
GC>ON_BN_CLICKED(IDC_MANUAL_MARKS, &CSewingDlg::OnBnClickedManualMarks)
GC>ON_EN_KILLFOCUS(IDC_PRECISION, &CSewingDlg::OnEnKillfocusPrecision)
GC>ON_EN_KILLFOCUS(IDC_STOCKOUT, &CSewingDlg::OnEnKillfocusStockout)
GC>ON_EN_KILLFOCUS(IDC_STOCKIN, &CSewingDlg::OnEnKillfocusStockin)
GC>ON_EN_KILLFOCUS(IDC_MARKING, &CSewingDlg::OnEnKillfocusMarking)
GC>ON_EN_CHANGE(IDC_MARKING, &CSewingDlg::OnEnChangeMarking)
GC>END_MESSAGE_MAP()

GC>//-----------------------------------------------------------------------------
GC>// Dialog initialization
GC>BOOL CSewingDlg::OnInitDialog()
GC>{
GC>    CDialog::OnInitDialog();

GC>    m_StockOutCheck = true;
GC>    m_StockInCheck = false;
GC>    m_MarkingCheck = true;
GC>    m_StockOut = 10.50;
GC>    m_StockIn  = 10.50;
GC>    m_MarkingStep = 100;
GC>    m_SelectType = 0;
GC>    m_MaxSplitNum = 20;
GC>    m_MaxMarksNum = 5;

GC>    ((CButton*)GetDlgItem( IDC_PROCESS_CLOSED_ONLY ))->SetCheck( m_ProcessClosedOnly? 1 : 0 );
GC>    ((CButton*)GetDlgItem( IDC_CONTOUR_HAS_ANY_LINE_STYLES ))->SetCheck( m_ContourHasAnyLineStyles? 1 : 0 );
GC>    ((CButton*)GetDlgItem( IDC_PROCESS_ENCLOSED_CONTUORS ))->SetCheck( m_ProcessEnclosedContours? 1 : 0 );

GC>    UpdateData(FALSE);

GC>    return TRUE;  // return TRUE unless you set the focus to a control
GC>                  // EXCEPTION: OCX Property Pages should return FALSE
GC>}


GC>/*
GC>    //reference obj = Circle(100,100,100,1);
GC>/ *
GC>    RectangleParam par; // структура параметров прямоугольника
GC>    ::memset(&par, 0, sizeof(RectangleParam));
       RectangleParam par = {}; // вместо memset'а, на котором слишком легко опечататься

GC>    par.x = -73.55; // базовая точка 1
GC>    par.y = 39.95; // 
GC>    par.ang = 0.00; // угол вектора направления от 1-ой точки ко 2-ой
GC>    par.height = -66.68; // высота
GC>    par.width = 79.90; // ширина
GC>    par.pCorner = ::CreateArray(CORNER_ARR, 0); // Создание массива параметров углов
GC>    par.style = 1; // стиль линии

GC>    reference obj = ::ksRectangle(&par, 0); // создать прямоугольник 

// этот reference владеющий или нет? логично предположить, что владеющий, тогда почему без умных указателей?

GC>* /
GC>/ *
GC>    reference rseg1 = LineSeg(10,10,100,100,1);
GC>    reference rseg2 = LineSeg(100,100,200,20,1);

GC>    reference obj = NewGroup(0);
GC>    AddObjGroup(obj, rseg1);
GC>    AddObjGroup(obj, rseg2);
GC>    EndGroup();* /

GC>    Contour(1);

GC>    //определим группу выделения
GC>//    reference obj0 = SelectGroup(0,2,0,0,0,0); //ksViewGetObjectArea();
GC>    //CopyObj(obj0, 0,0,0,0,1,1);
GC>    //CopyGroupToDocument(obj0, ksGetCurrentDocument(0), ksGetCurrentDocument(0));

GC>//    reference group_iterator = CreateIterator(LINESEG_OBJ, 0);
GC>    reference group_iterator = CreateIterator(SELECT_GROUP_OBJ, 0);

GC>    reference cur_obj = MoveIterator(group_iterator, 'F');
GC>    while (cur_obj)
GC>    {
GC>        ksLineSegParamPtr lspLineParam = kompasAPI->GetParamStruct(ko_LineSegParam);
GC>//        long ksres = sewing_object->m_pKompasDoc5->ksGetObjParam(cur_obj, lspLineParam, ALLPARAM);
GC>        long ksres = m_pKompasDoc5->ksGetObjParam(cur_obj, lspLineParam, ALLPARAM);
GC>        if(ksres)
GC>        {
GC>            double x1 = lspLineParam->x1,
GC>                   x2 = lspLineParam->x2,
GC>                   y1 = lspLineParam->y1,
GC>                   y2 = lspLineParam->y2;

GC>//            LineSeg(x1,y1,x2,y2,1);
GC>/ * задание параметров копирования * /
GC>/ *
GC>RequestInfo info;
GC>memset(&info, 0, sizeof(info));
GC>info.title = "Title";
GC>info.prompt = " Новое положение базовой точки ";
GC>info.dynamic = 0;
GC>info.commands = "!test!test1";
GC>int j=CommandWindow(&info);

GC>double x=0,y=0,scale=1,ang=0;
GC>if ((Cursor (&info, &x, &y, 0)) &&
GC>(ReadDouble("Угол поворота", 0, 0, 360, &ang)) &&
GC>(ReadDouble("Масштаб", 1, 0, 999, &scale)))
GC>;* /
GC>/ *
GC>reference posLeader = ksCreateViewObject(POSLEADER_OBJ );
GC>if (posLeader){ 
GC>LightObj(posLeader, 1); 
GC>Message("Позиционная линия выноски");
GC>LightObj(posLeader, 0);

GC>    if (ksEditViewObject(posLeader)) { 
GC>    LightObj(posLeader, 1);
GC>    Message("Отредактированная позиционная линия выноски"); 
GC>    LightObj(posLeader, 0);
GC>    } 

GC>} 

GC>

Извини, дальше смотреть код было уже лень. Во-первых, его слишком много, во-вторых, он замусорен /**/ и плохой табуляцией, — как черновик, сойдёт, а на публику такое выносить не очень прилично.
Перекуём баги на фичи!
Re: Оцените качество кода на С++
От: -n1l-  
Дата: 18.09.14 10:28
Оценка:
Здравствуйте, GhostCoders, Вы писали:
Первый вопрос, у вас есть code conventions в вашей компании? Или все пишут как им хочется?

Почему в одном месте скобки стоят в другом нет? Это создает трудности в чтении.

GC>    else {
GC>        if ( absmax < minerror ) bx = true;
GC>            else bx = approx_equals<double>( x1/absmax, x2/absmax, correction );
GC>    }


Понятное дело что так можно, но...


Вот это что за апи метода?
void AdviseDoc( KompasObject* kompas,
                LPDISPATCH doc, long docType,
                bool fSelectMng = true,
                bool fObject = true,
                bool fStamp = true,
                bool fDocument = true,
                bool fSpecification = true,
                bool fSpcObject = true,
                long objType = 0 /*= -1*/);


Как это наверное здорово читается:

...
                false, true,true,false,false,...

Каким аргументам я задал значение а каким нет, гадайте. Да?


            do {    // pseudo-cycle to destruct tempRasters BEFORE doc-switching & CloseDocument()
                KompasIteratorHolder tempApprox(CreateIterator(ALL_OBJ, 0));    // GetViewReference(0)));
                if(!tempApprox) break;
                elem = MoveIterator(tempApprox, 'F');
            } while (0);


вот это что за цикл на одно действие?

Код сложно читать -> поддерживать, есть риск получить трудноуловимые ошибки.
В коде присутствуют слишком универсальные классы.
Код, может быть и сойдет, но явно говорить, что он хорош — это глупо.
Re: Оцените качество кода на С++
От: Andrew.W Worobow https://github.com/Worobow
Дата: 18.09.14 10:43
Оценка:
Здравствуйте, GhostCoders, Вы писали:

GC>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.


Если чесно, то код ужас. Как по стилю, так и по подпоркам и затычкам.
Но так сейчас многие пишут. Какие-то странные имена Типа GjrThowAllocWPgo, ифы типа

if ( ! (a!=0) )e=4;
break;


Нет разбивки на файлы, мало коментариев. мало пустых строк — код НЕ оформлен. Всякая экзотика в виде запятых.
Но я бы попросил его оформить как надо. И все. Ну в смысле не называл это плохим кодом.
Не все кто уехал, предал Россию.
Re: Оцените качество кода на С++
От: bnk СССР http://unmanagedvisio.com/
Дата: 18.09.14 11:11
Оценка:
Здравствуйте, GhostCoders, Вы писали:

GC>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.


Я думаю что с форматированием проблемы из-за смешения табов и пробелов.
Т.е. у тебя просто размер табуляции стоит не такой, как у коллеги, поэтому выглядит жутковато.

Код конечно не торт, но подобный ппц вполне ожидаем в продукте с N-летней историей.

В общем зря ты на него бочку катишь, "переписать все заново" — это скорее мечта,
а ему премию выдать надо за то что он в этом разбирается, или по-крайней мере молоко за вредность
Отредактировано 18.09.2014 11:12 bnk . Предыдущая версия .
Re[2]: Оцените качество кода на С++
От: GhostCoders Россия  
Дата: 18.09.14 11:20
Оценка:
Здравствуйте, bnk, Вы писали:

bnk>Код конечно не торт, но подобный ппц вполне ожидаем в продукте с N-летней историей.

Этот код был написан в течении последних двух месяцев практически с нуля. Так сказать, свежачок....
Третий Рим должен пасть!
Re[3]: Оцените качество кода на С++
От: GhostCoders Россия  
Дата: 18.09.14 11:27
Оценка:
Здравствуйте, niXman, Вы писали:

X>и да, функции размером в двести строк, тоже доставляют %)

Тут есть функция CSewingDlg::PseudoProcessAcceptedData(), которая начинается на 2029 строке и заканчивается на строке номер 3433
Третий Рим должен пасть!
Re[3]: Оцените качество кода на С++
От: smeeld  
Дата: 18.09.14 11:30
Оценка: 3 (1)
Здравствуйте, niXman, Вы писали:

X>и да, функции размером в двести строк, тоже доставляют


Жалкие двести строк и кому-то не нравится?
Видели бы вы такие функции
Re[4]: Оцените качество кода на С++
От: niXman Ниоткуда https://github.com/niXman
Дата: 18.09.14 11:35
Оценка: +1
Здравствуйте, smeeld, Вы писали:

S>Видели бы вы такие функции

эта функция лучше, потому что в ней комментариев с избытком.
но да, можно было и ее разделить на *надцать поменьше — все бы только выиграли
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[3]: Оцените качество кода на С++
От: smeeld  
Дата: 18.09.14 11:35
Оценка:
Здравствуйте, GhostCoders, Вы писали:

GC>Здравствуйте, bnk, Вы писали:


bnk>>Код конечно не торт, но подобный ппц вполне ожидаем в продукте с N-летней историей.

GC>:) Этот код был написан в течении последних двух месяцев практически с нуля. Так сказать, свежачок....

Это всё писалось целых два месяца? А как же план для программиста в 500 строк в день?
Re[2]: Оцените качество кода на С++
От: Кодт Россия  
Дата: 18.09.14 11:36
Оценка:
Здравствуйте, -n1l-, Вы писали:

N>
N>            do {    // pseudo-cycle to destruct tempRasters BEFORE doc-switching & CloseDocument()
N>                KompasIteratorHolder tempApprox(CreateIterator(ALL_OBJ, 0));    // GetViewReference(0)));
N>                if(!tempApprox) break;
N>                elem = MoveIterator(tempApprox, 'F');
N>            } while (0);
N>

N>вот это что за цикл на одно действие?

Это прикольнейшая идиома. Это не цикл, а goto end. В роли goto используется break.

N>Код сложно читать -> поддерживать, есть риск получить трудноуловимые ошибки.


(в смысле — всё плохо!)
Перекуём баги на фичи!
Re[3]: Оцените качество кода на С++
От: Кодт Россия  
Дата: 18.09.14 11:38
Оценка: +1 :))
Здравствуйте, GhostCoders, Вы писали:

bnk>>Код конечно не торт, но подобный ппц вполне ожидаем в продукте с N-летней историей.

GC> Этот код был написан в течении последних двух месяцев практически с нуля. Так сказать, свежачок....

"Просто я её сочинил как древнюю легенду"
Перекуём баги на фичи!
Re[4]: Оцените качество кода на С++
От: GhostCoders Россия  
Дата: 18.09.14 11:47
Оценка:
Здравствуйте, smeeld, Вы писали:

S>Жалкие двести строк и кому-то не нравится?

S>Видели бы вы такие функции
Мой сотрудник на самом деле не намного отстал.
Тут есть функция CSewingDlg::PseudoProcessAcceptedData(), которая начинается на 2029 строке и заканчивается на строке номер 3433
Третий Рим должен пасть!
Re: Оцените качество кода на С++
От: TimurSPB Интернет  
Дата: 18.09.14 11:53
Оценка:
Сурово:
for (ULONG i0 = 0, j0 = 0; j0 < sabNewArray.cElements; i0 += 6 * step, j0 += 6)      // B,L,R -> L,B,R !
            {
                if (i0 >= 6 * AllPointsCount) i0 = 6 * (AllPointsCount - 1);
                LONG i = i0, j = j0 + 2;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
                i++, j++;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
                i = i0 + 2, j = j0;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
                i++, j++;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
                i = i0 + 4, j = j0 + 4;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
                i++, j++;
                ::SafeArrayGetElement( pSrc, &i, &arrdata);
                ::SafeArrayPutElement( pSafe, &j, &arrdata);
            }

Не хотелось бы такое поддерживать в будущем. Код без автора я бы править не стал.
Ещё смущает обилие конструкций "Points.push_back(.." в вектор на стеке. Производительность кода всех устраивает?
Make flame.politics Great Again!
Re: Оцените качество кода на С++
От: vladimir_i СССР  
Дата: 18.09.14 12:20
Оценка: -6
Здравствуйте, GhostCoders, Вы писали:

GC>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.

GC>...

Довольно странно, что вы вот так просто выставляете собственность компании на всеобщее обозрение. Если конечно это не ваша компания, но все равно странно как-то.
Может это даже ваш код и, вместо того чтобы в этом признаться и показаться не очень профессиональным в глазах коллег, вы придумали сотрудника.


По существу: текст не помешает пропустить сквозь авто-формат.
Еще можно провести рефакторинг: сделать наименование однообразным, например, окончательно определиться с "m_" в именах членов класса:
 int equi_curves_count;
 bool m_swapped;


А вообще, качество кода определяется наличием ошибок и соответствием ТЗ. Если программа делает то, что требуется, не течет и не тормозит, то нечего придираться.
Re[2]: do { ... } while(false);
От: Qbit86 Кипр
Дата: 18.09.14 12:26
Оценка:
Здравствуйте, -n1l-, Вы писали:

N>
N>            do {    // pseudo-cycle to destruct tempRasters BEFORE doc-switching & CloseDocument()
N>                KompasIteratorHolder tempApprox(CreateIterator(ALL_OBJ, 0));    // GetViewReference(0)));
N>                if(!tempApprox) break;
N>                elem = MoveIterator(tempApprox, 'F');
N>            } while (0);
N>


N>вот это что за цикл на одно действие?


Стандартная императивная конструкция, предоставляет блок (scope), из которого можно выйти по break. Также широко используется в препроцессорных дефайнах, если нужен блок, но { ... } использовать нежелательно.
Глаза у меня добрые, но рубашка — смирительная!
Re: Оцените качество кода на С++
От: Константин Россия  
Дата: 18.09.14 12:39
Оценка: +2 -1
Здравствуйте, GhostCoders, Вы писали:

GC>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.


GC>
GC>    KompasObjectHolder(KompasObjectHolder& prev_holder) : m_reference(prev_holder.m_reference) { prev_holder.m_reference = 0; }
GC>    KompasObjectHolder& operator =(KompasObjectHolder& prev_holder) {
GC>        if (!m_reference) m_reference = prev_holder.m_reference, prev_holder.m_reference = 0; return *this; }
GC>


Если компилятор поддерживает C++11, то такое писать просто непростительно. Для старых компилятор код тоже "с душком".
Re[3]: do { ... } while(false);
От: -n1l-  
Дата: 18.09.14 12:41
Оценка:
Здравствуйте, Qbit86, Вы писали:
Q>Стандартная императивная конструкция, предоставляет блок (scope), из которого можно выйти по break. Также широко используется в препроцессорных дефайнах, если нужен блок, но { ... } использовать нежелательно.

Ну конкретно тут в чем плюс его использования?
Понимаю если бы там макросы были, а так...
Re[3]: do { ... } while(false);
От: Zhendos  
Дата: 18.09.14 12:44
Оценка: +1
Здравствуйте, Qbit86, Вы писали:

Q>Здравствуйте, -n1l-, Вы писали:


N>>
N>>            do {    // pseudo-cycle to destruct tempRasters BEFORE doc-switching & CloseDocument()
N>>                KompasIteratorHolder tempApprox(CreateIterator(ALL_OBJ, 0));    // GetViewReference(0)));
N>>                if(!tempApprox) break;
N>>                elem = MoveIterator(tempApprox, 'F');
N>>            } while (0);
N>>


N>>вот это что за цикл на одно действие?


Q>Стандартная императивная конструкция, предоставляет блок (scope), из которого можно выйти по break. Также широко используется в препроцессорных дефайнах, если нужен блок, но { ... } использовать нежелательно.


Но разве нельзя было написать так?
    {
        KompasIteratorHolder tempApprox(CreateIterator(ALL_OBJ, 0));
        if(tempApprox)
             elem = MoveIterator(tempApprox, 'F');
     }


Разве не намного проще?

И насчет стандартности я бы поспорил, да трюк do {} while (false) для макросов
это стандартная идиома,
но вот блок для которого можно выйти по "break",
редко встречающаяся.
Re[3]: Оцените качество кода на С++
От: -n1l-  
Дата: 18.09.14 12:46
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, -n1l-, Вы писали:


N>>
N>>            do {    // pseudo-cycle to destruct tempRasters BEFORE doc-switching & CloseDocument()
N>>                KompasIteratorHolder tempApprox(CreateIterator(ALL_OBJ, 0));    // GetViewReference(0)));
N>>                if(!tempApprox) break;
N>>                elem = MoveIterator(tempApprox, 'F');
N>>            } while (0);
N>>

N>>вот это что за цикл на одно действие?

К>Это прикольнейшая идиома. Это не цикл, а goto end. В роли goto используется break.


И типа if тут никак? Чем это хуже?


KompasIteratorHolder tempApprox(CreateIterator(ALL_OBJ, 0));
if(tempApprox){
 elem = MoveIterator(tempApprox, 'F');
}
Отредактировано 18.09.2014 12:46 -n1l- . Предыдущая версия .
Re[4]: do { ... } while(false);
От: Qbit86 Кипр
Дата: 18.09.14 12:52
Оценка: +1
Здравствуйте, Zhendos, Вы писали:

Z>Разве не намного проще?


Возможно, перед нами артефакт незавершённого упрощения — изначально блок был сложнее и содержал больше выходов по break.

Z>но вот блок для которого можно выйти по "break",

Z>редко встречающаяся.

Иногда встречается в длинных многошаговых инициализациях, где по break осуществляется фоллбэк, аналог goto-перехода к метке error.
Глаза у меня добрые, но рубашка — смирительная!
Re[4]: Оцените качество кода на С++
От: Кодт Россия  
Дата: 18.09.14 13:27
Оценка:
Здравствуйте, -n1l-, Вы писали:

К>>Это прикольнейшая идиома. Это не цикл, а goto end. В роли goto используется break.

N>И типа if тут никак? Чем это хуже?

Там эта идиома неоднократно встречается
do {
  init_x();
  if(x_is_bad) break;

  init_y();
  if(y_is_bad) break;

  init_z();
  if(z_is_dead) break;

  xyz();

} while(false);
cleanup_xyz();
Перекуём баги на фичи!
Re[4]: do { ... } while(false);
От: Andrew.W Worobow https://github.com/Worobow
Дата: 18.09.14 13:48
Оценка:
Здравствуйте, -n1l-, Вы писали:

N>Ну конкретно тут в чем плюс его использования?


Вообще по правильному ( в теории, но не на практике ) вместо такого while(1) {;;; break;} или do {}while(0); надо делать функцию в которой уже выходить на end просто по return.
Но на практике особенно там где код не оптимизируют ( ядро например ) но производительность требуется используют такой вот метод.

while (1){
   HANDLE h = open();
   if ( !h )
      break;
   while (1){
      s=write(h,"{");
      if ( !s )
         break;
      
      write(h,"abc,def,uuu");

      write(h,"}");
      break;
   }
   close(h); 
   break;
}


то есть это по уму, по идеализму надо заменять на функцию даже если она один раз вывывается, но еще не понятно что яснее — функция которая написана только для того чтобы брековаться по return вместо goto.

То есть есть такие операции которые требуют "операций скобок" — открыл -> надо закрыть, записал начало "{" надо записть и конец "}". И так делее.

В этом случае в конце кода действий возникает логика — а открыли мы или нет, и если открыли надо закрыть. И так далее.

Но если применять логику иную — типа создавать объект у которого есть свойсто "открыть" и есть констуктор и есть деструктор то тогда такая логика с проверкой а мы открыли или нет в том же деструкторе выглядит логично. Но если это просто небольшой код, и нет нужды городить обьекты и классы, ну как я уже сказал в ядре например. Где эфективность важнее красоты, а оптимизации от компилятора нет, то пишут вот так — ну не очень красиво, но по привыкнешь — нормально.


N>Понимаю если бы там макросы были, а так...
Не все кто уехал, предал Россию.
Отредактировано 18.09.2014 13:54 Andrew.W Worobow . Предыдущая версия .
Re[5]: Оцените качество кода на С++
От: -n1l-  
Дата: 18.09.14 13:55
Оценка:
Здравствуйте, Кодт, Вы писали:
Воу воу воу, сама то идиома причем? Ваш пример еще куда ни шло, но там неуместно, имхо.
Re[5]: do { ... } while(false);
От: -n1l-  
Дата: 18.09.14 13:56
Оценка: +1
Да я знаю о том, что еще и циклы можно размотать, но, не везде же их разматывать.
Ну серьезно. Ну не нужен там этот код.
Re[6]: Оцените качество кода на С++
От: uzhas Ниоткуда  
Дата: 18.09.14 14:15
Оценка: +3
Здравствуйте, -n1l-, Вы писали:

N>Воу воу воу, сама то идиома причем? Ваш пример еще куда ни шло, но там неуместно, имхо.


никуда не шло
эта "идиома" в плюсовом коде не нужна. в старом добром Си — на здоровье
Re[7]: Оцените качество кода на С++
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 18.09.14 14:31
Оценка:
Здравствуйте, uzhas, Вы писали:

U>эта "идиома" в плюсовом коде не нужна. в старом добром Си — на здоровье

Предложи наглядное решение на С++. Мне вот интересно.
Sic luceat lux!
Re[5]: Оцените качество кода на С++
От: PM  
Дата: 18.09.14 14:34
Оценка: +3
Здравствуйте, Кодт, Вы писали:

К>>>Это прикольнейшая идиома. Это не цикл, а goto end. В роли goto используется break.

N>>И типа if тут никак? Чем это хуже?

По-моему этой идиоме место только в коде на C и только для идеологических противников goto cleanup
Re[8]: Оцените качество кода на С++
От: Zhendos  
Дата: 18.09.14 14:39
Оценка:
Здравствуйте, Kernan, Вы писали:

K>Здравствуйте, uzhas, Вы писали:


U>>эта "идиома" в плюсовом коде не нужна. в старом добром Си — на здоровье



  init_x();
  if(x_is_bad) break;

  init_y();
  if(y_is_bad) break;

  init_z();
  if(z_is_dead) break;

  xyz();

} while(false);
cleanup_xyz();


K>Предложи наглядное решение на С++. Мне вот интересно.


X x;
Y y;
Z z;
xyz();
Re[6]: Оцените качество кода на С++
От: Кодт Россия  
Дата: 18.09.14 14:42
Оценка:
Здравствуйте, -n1l-, Вы писали:

N>Воу воу воу, сама то идиома причем? Ваш пример еще куда ни шло, но там неуместно, имхо.


Я указал на то, что человек её не единожды использовал. В конкретно том месте это была дань единообразию, я так думаю
Перекуём баги на фичи!
Re[9]: Оцените качество кода на С++
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 18.09.14 14:50
Оценка:
Здравствуйте, Zhendos, Вы писали:

Z>
Z>X x;
Z>Y y;
Z>Z z;
Z>xyz();
Z>

try-catch забыл. Я не особо смотрел на код, но вроде после do-while было что-то ещё.
Sic luceat lux!
Re: Оцените качество кода на С++
От: _Dreamer Россия  
Дата: 18.09.14 14:50
Оценка: +3
Здравствуйте, GhostCoders, Вы писали:

GC>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.


Вы еще попробуйте код показать на http://codereview.stackexchange.com/
Я думаю, там будет устроен разнос.
На мой взгляд, код ужасен.
Чтобы не быть субъективным, нужно принять стандарт оформления кода и встроить линтер на прекоммит.
Тогда никто не сможет писать в одном месте "m_", "_" или просто "someMemberName".
Принять решение о написании тестов. Тогда никто не будет писать таких огромных классов.
Использовать VCS, их куча на выбор, тогда не будет никаких закомментированных участков кода.
Организовать код-ревью если не каждого коммита в master, то хотя бы важных фич.
Тогда Вы сможете аргументировать.

Я понимаю, что это может показаться сложным и довольно дорогим в смысле времени и денег.
Но представьте, сколько Вы готовы потратить на содержание сотрудников, которые пишут ужасный код, но разбираются в нем, и поэтому их не уволить?
На отладке, потому что нет тестов и все проверяется вручную?
На поддержке, потому что нужно по пеплу орла и кофейной гуще гадать что сломалось у клиента?
Re[10]: Оцените качество кода на С++
От: PM  
Дата: 18.09.14 14:54
Оценка:
Здравствуйте, Kernan, Вы писали:

K>Здравствуйте, Zhendos, Вы писали:


Z>>
Z>>X x;
Z>>Y y;
Z>>Z z;
Z>>xyz();
Z>>

K>try-catch забыл. Я не особо смотрел на код, но вроде после do-while было что-то ещё.

try-catch здесь не нужен, т.к. в С++ коде X, Y, Z должны использовать идиому RAII
Re[10]: Оцените качество кода на С++
От: Zhendos  
Дата: 18.09.14 14:58
Оценка: +2
Здравствуйте, Kernan, Вы писали:

K>Здравствуйте, Zhendos, Вы писали:


Z>>
Z>>X x;
Z>>Y y;
Z>>Z z;
Z>>xyz();
Z>>

K>try-catch забыл. Я не особо смотрел на код, но вроде после do-while было что-то ещё.

не забыл, а не поставил.
т.к. что и куда нужно возвращать из контекста не ясно,
то просто не ловим исключения, пусть они идут наверх
Re[7]: Оцените качество кода на С++
От: -n1l-  
Дата: 18.09.14 15:17
Оценка:
В принципе я тоже так считаю, для макросов норм, но так не очень.
Re: Оцените качество кода на С++
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 18.09.14 15:18
Оценка:
Здравствуйте, GhostCoders, Вы писали:

GC>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.

Плохое форматирование, проблемы с конвенциями именования.
Bad smells of code прямо по фаулеру: дубликаты, магические числа, длинные метода.
Очень плохо с архитектурой: нарушение SRP, не выделены классы которые прямо-таки напрашиваются на создание (вроде Point)Думаю, не помешало бы использовать пару структурных паттёрнов.
Ещё мне кажется, что человек не знает о назначении ASSERT.
Из плюсов, человек знает как про double и его проблемы.
Sic luceat lux!
Re[11]: Оцените качество кода на С++
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 18.09.14 15:22
Оценка:
Здравствуйте, Zhendos, Вы писали:

Z>т.к. что и куда нужно возвращать из контекста не ясно,

Z>то просто не ловим исключения, пусть они идут наверх
Ну ок, ок. Напрягает то, что сущностей X, Y, Z как-то не выделено.
Sic luceat lux!
Re[2]: Оцените качество кода на С++
От: -n1l-  
Дата: 18.09.14 15:24
Оценка:
Здравствуйте, _Dreamer, Вы писали:

_D>На мой взгляд, код ужасен.

Плюсую.
_D>Чтобы не быть субъективным, нужно принять стандарт оформления кода и встроить линтер на прекоммит.
Дико плюсую.
_D>Принять решение о написании тестов. Тогда никто не будет писать таких огромных классов.
Заплюсовываю!
_D>Использовать VCS, их куча на выбор, тогда не будет никаких закомментированных участков кода.
Там скорее всего есть, встречается такое, что в ветках комменты.
_D>Организовать код-ревью если не каждого коммита в master, то хотя бы важных фич.
Просто дико заплюсовываю. Нигде не видел системы codereview, там где работал, кроме одного проекта QT'a.
Когда был контрибьютером, просто офигел от того, как четко и правильно работают люди.
_D>Тогда Вы сможете аргументировать.
Согласен, поддерживаю, всецело отдаю свой голос за.
Re[2]: Оцените качество кода на С++
От: -n1l-  
Дата: 18.09.14 15:35
Оценка:
Здравствуйте, _Dreamer, Вы писали:

встроить линтер на прекоммит.

Так и называется, "Линтер"? В svn такое есть?
Re[4]: Оцените качество кода на С++
От: Ops Россия  
Дата: 18.09.14 15:41
Оценка:
Здравствуйте, -n1l-, Вы писали:

N>>>
N>>>            do {    // pseudo-cycle to destruct tempRasters BEFORE doc-switching & CloseDocument()
N>>>                KompasIteratorHolder tempApprox(CreateIterator(ALL_OBJ, 0));    // GetViewReference(0)));
N>>>                if(!tempApprox) break;
N>>>                elem = MoveIterator(tempApprox, 'F');
N>>>            } while (0);
N>>>

N>>>вот это что за цикл на одно действие?

К>>Это прикольнейшая идиома. Это не цикл, а goto end. В роли goto используется break.


N>И типа if тут никак? Чем это хуже?


N>

N>KompasIteratorHolder tempApprox(CreateIterator(ALL_OBJ, 0));
N>if(tempApprox){
N> elem = MoveIterator(tempApprox, 'F');
N>}

N>


Не вникал в остальной код, но здесь разное время жизни tempApprox. В первом случае он уничтожается в конце блока, в твоем — живет себе дальше. Но твой вариант можно в еще один блок пихнуть.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Re[10]: Оцените качество кода на С++
От: smeeld  
Дата: 18.09.14 17:25
Оценка: +1 -4
Здравствуйте, Kernan, Вы писали:

K>Здравствуйте, Zhendos, Вы писали:


K>try-catch забыл. Я не особо смотрел на код, но вроде после do-while было что-то ещё.


Эти С++-ники такие С++-ники. Кто-то думает что с try{}catch будет производительнее?
В try{}catch произойдёт вызов __cxa_throw в котором вызовется __Unwind_RaiseException,
а там, мерзопакостный

_Unwind_RaiseException(struct _Unwind_Exception *exc)
{
..................................................
  /* Phase 1: Search.  Unwind the stack, calling the personality routine
     with the _UA_SEARCH_PHASE flag set.  Do not modify the stack yet.  */
  while (1)
    {
      _Unwind_FrameState fs;

      /* Set up fs to describe the FDE for the caller of cur_context.  The
     first time through the loop, that means __cxa_throw.  */
      code = uw_frame_state_for (&cur_context, &fs);

      if (code == _URC_END_OF_STACK)
    /* Hit end of stack with no handler found.  */
    return _URC_END_OF_STACK;

      if (code != _URC_NO_REASON)
    /* Some error encountered.  Usually the unwinder doesn't
       diagnose these and merely crashes.  */
    return _URC_FATAL_PHASE1_ERROR;

      /* Unwind successful.  Run the personality routine, if any.  */
      if (fs.personality)
    {
      code = (*fs.personality) (1, _UA_SEARCH_PHASE, exc->exception_class,
                    exc, &cur_context);
      if (code == _URC_HANDLER_FOUND)
        break;
      else if (code != _URC_CONTINUE_UNWIND)
        return _URC_FATAL_PHASE1_ERROR;
    }

      /* Update cur_context to describe the same frame as fs.  */
      uw_update_context (&cur_context, &fs);
    }

  /* Indicate to _Unwind_Resume and associated subroutines that this
     is not a forced unwind.  Further, note where we found a handler.  */
  exc->private_1 = 0;
  exc->private_2 = uw_identify_context (&cur_context);

  cur_context = this_context;
  code = _Unwind_RaiseException_Phase2 (exc, &cur_context);
  if (code != _URC_INSTALL_CONTEXT)
    return code;

  uw_install_context (&this_context, &cur_context);
}

Но кроме этого ещё куча работы и куча вызовов.
Может лучше сразу while(1){ ... break/return } ?
Re: Оцените качество кода на С++
От: landerhigh Пират  
Дата: 18.09.14 18:14
Оценка: +1 -2 :)
Здравствуйте, GhostCoders, Вы писали:

GC>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.


Проводим, значит, код ревью

GC>
GC>// SewingDlg.h
GC>


У вас coding conventions вообще есть? где оформленный заголовок с именем файла, автором, датой создания, названием проекта?

GC>
GC>#ifdef max
GC>#undef max
GC>#endif

GC>#ifdef min
GC>#undef min
GC>#endif
GC>


Вот это нужно комментировать.

GC>
GC>class KompasObjectHolder
GC>


Стоп. Безотносительно того, что было уже сказано по поводу частокола конструкторов: Где комментарии? Где разъяснение, зачем это нужно. Где обоснование невозможности генеразизации класса (шаблоны)? И, чтобы уж добить, зачем оно вообще надо, если есть smart pointers?

GC>
GC>class KompasIteratorHolder : public KompasObjectHolder
GC>


Все то же самое плюс — почему в одном заголовочном файле объявлено более одного класса?

GC>
GC>class CSewingDlg : public CDialog       // диалоговое окно CSewingDlg
GC>


Офигенный комментарий. Спасибо, кэп.

GC>
GC>    CSewingDlg(CWnd* pParent = NULL);   // стандартный конструктор
GC>


Офигенный комментарий. Спасибо, кэп.

Дальнейшие поскипанные объявления методов тоже не проходят ревью по поводу отсутствия сколько-нибудь внятной документации. Об универсальном всемогуторе поговорим позже.

GC>
GC>reference CSewingDlg::Draw2DElement(reference element, bool closed)
GC>{
GC>    if (ReturnResult()) ResultNULL();
GC>    if (!ExistObj(element)) return 0;

GC>    switch (GetObjParam(element, 0, 0, ALLPARAM))
GC>    {
GC>    case CONTOUR_OBJ:
GC>        return DrawContour(element);        // ONLY APPROXIMATION NOW
GC>    case LINESEG_OBJ:
// Вызовы отрисовок для остальных типов фигур в ужасе поскипаны
GC>        break;
GC>    }
GC>    return 0;
GC>}
GC>



А это что вообще такое? Такое слово, как инкапсуляция, Вашему подчиненному вообще знакомо?
Это не C++ код, это страшное проявление C в его худшем виде, которое нынче еще поискать надо.

На этом в нашей компании код ревью бы прекратился за его дальнейшей бессмысленностью.
www.blinnov.com
Re[2]: Оцените качество кода на С++
От: Abyx Россия  
Дата: 18.09.14 18:43
Оценка: +3
Здравствуйте, landerhigh, Вы писали:

GC>>
GC>>// SewingDlg.h
GC>>


L>У вас coding conventions вообще есть? где оформленный заголовок с именем файла, автором, датой создания, названием проекта?

для всего этого есть система контроля версий.
есть смысл разве что писать копирайт, и то не очень-то нужно.

GC>>
GC>>#ifdef max
GC>>#undef max
GC>>#endif

GC>>#ifdef min
GC>>#undef min
GC>>#endif
GC>>


L>Вот это нужно комментировать.

не нужно. те кто пишут под винду и так знают про эту проблему. другое дело что лучше NOMINMAX

GC>>
GC>>class KompasObjectHolder
GC>>

L>Стоп. Безотносительно того, что было уже сказано по поводу частокола конструкторов: Где комментарии? Где разъяснение, зачем это нужно.
кажется имя класса и так говорит о том зачем это нужно.

GC>>
GC>>class KompasIteratorHolder : public KompasObjectHolder
GC>>


L>Все то же самое плюс — почему в одном заголовочном файле объявлено более одного класса?

а почему бы и нет? особенно если классы используются вместе
In Zen We Trust
Re[11]: Оцените качество кода на С++
От: Zhendos  
Дата: 18.09.14 18:46
Оценка:
Здравствуйте, smeeld, Вы писали:

S>Здравствуйте, Kernan, Вы писали:


K>>Здравствуйте, Zhendos, Вы писали:


K>>try-catch забыл. Я не особо смотрел на код, но вроде после do-while было что-то ещё.


S>Эти С++-ники такие С++-ники. Кто-то думает что с try{}catch будет производительнее?

S>В try{}catch произойдёт вызов __cxa_throw в котором вызовется __Unwind_RaiseException,
S>а там, мерзопакостный

Непонятно с чем вы спорите. В коде с "do {} while" обрабатываются ошибки выделения памяти,
т.е. вероятность выброса исключения очень мала,
код для бросания исключений и их обработки в частности ваш "__Unwind_RaiseException",
будет вызван только в случае нештатной ситуации, иными словами
в данном случае, с использованием современных C++ компиляторов,
измененный код будет работать не медленнее чем первоночальный,
а возможно и быстрее.

Конечно, если вероятность не получить какой-то ресурс велика,
то придется писать по другому, например сначала,
захватывая ресурс, и если нет ошибок передача его объекту класса
обертки, который освободит ресурс в деструкторе. Тем самым
избавляясь от необходимости кидать исключение из конструктора объекта обертки.
Re[12]: Оцените качество кода на С++
От: smeeld  
Дата: 18.09.14 18:57
Оценка: -1
Здравствуйте, Zhendos, Вы писали:

Выше брезгливо морщились от выхода из последовательности операций, при
наступлении ошибки на каком-то этапе, с помощью do{ ...break..}while(1)
и говорилось про необходимость использовать это try{ throw x }catch();
А это как бы менее производительно, чем вариант с while{ break};
И вызов __Unwind_RaiseException произойдёт при всяком throw, ибо как есть стандарт.
Re[2]: Оцените качество кода на С++
От: MTD https://github.com/mtrempoltsev
Дата: 18.09.14 19:03
Оценка: -4 :))
Здравствуйте, Andrew.W Worobow, Вы писали:

AWW>мало коментариев.


В хорошем коде комментариев быть не должно.
Re[3]: Оцените качество кода на С++
От: landerhigh Пират  
Дата: 18.09.14 19:04
Оценка: +1 -1
Здравствуйте, Abyx, Вы писали:


L>>У вас coding conventions вообще есть? где оформленный заголовок с именем файла, автором, датой создания, названием проекта?

A>для всего этого есть система контроля версий.

Иногда, читай вероятность этого стремится к единице, информация об истории может потеряться. Происходит это при переезде на другую систему контроля версий или при просто копипасте файла из одного места в другое.

A>есть смысл разве что писать копирайт, и то не очень-то нужно.


Заголовок с копирайтом позволяет сразу понять, что это, откуда оно взялось и кто в этом виноват. Даже когда работаешь из движущегося поезда без подключения к системе. Но я не настаиваю, хозяин-барин, это и правда не так уж и важно.

GC>>>
GC>>>#ifdef max
GC>>>#undef max

GC>>>


L>>Вот это нужно комментировать.

A>не нужно. те кто пишут под винду и так знают про эту проблему. другое дело что лучше NOMINMAX

Я знаю. Это не отменяет того, что хорошей инженерной практикой является комментирование подобного. Даже если это всем известно, это не перестает быть грязным хаком.

GC>>>
GC>>>class KompasObjectHolder
GC>>>

L>>Стоп. Безотносительно того, что было уже сказано по поводу частокола конструкторов: Где комментарии? Где разъяснение, зачем это нужно.
A>кажется имя класса и так говорит о том зачем это нужно.

Комментарий должен быть. Он должен обосновывать необходимость этого холдера, в т.ч. содержать объяснение, почему аффтар не стал использовать умный указатель вместо этого, а также рассказывать, с какого такого бодуна объект вдруг стал называться ссылкой и зачем ему может понадобиться внешний удалятор. Любое инженерное решение должно быть обосновано. У меня сразу возникает вопрос — зачем нужно убивать объект, который является членом класса, неким внешним функтором. Если бы автор потрудился это описать в комментарии, он бы примерно на полпути, скорее всего, догадался бы, что инкапсуляция — не просто красивое слово, которое непонятно как правильно транслителировать на русском языке, а весьма правильный подход к проектированию подобных классов. Ну или бы четко описал, почему это должно быть сделано именно так, а не иначе.

GC>>>
GC>>>class KompasIteratorHolder : public KompasObjectHolder
GC>>>


L>>Все то же самое плюс — почему в одном заголовочном файле объявлено более одного класса?

A>а почему бы и нет? особенно если классы используются вместе

В итоге все классы в программе используются вместе так или иначе. Это не повод всех их запихивать в один файл.
www.blinnov.com
Re: Оцените качество кода на С++
От: Шахтер Интернет  
Дата: 18.09.14 19:13
Оценка:
Здравствуйте, GhostCoders, Вы писали:

GC>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.


Не стоит тащить на форум огромный код для его разбора. Достаточно небольшого фрагмента.

Разбирать код по принципу "я евангелист и этот код не вписывается в моё евангелие" не стоит.

Если код делается в команде, то должны быть выработаны командные требования по различным аспектам, включая оформление кода. Если этого сделано не было, то жаловаться на плохое оформление не стоит.

Прежде чем давать оценку, стоит честно ответить на вопросы: 1) этот код решает проставленную задачу?, 2) есть ли в нем ошибки?, 2) не жрет ли он неоправданно много ресурсов?

Если код работоспособен, то во многих случаях заниматься превращением его в памятник программистскому искусству не стоит. Иначе поставят памятник вашей компании. Посмертный.

Если же очень хочется и денег заработать, и шедевр написать, то начинать надо всё-таки с проработки хорошей архитектуры.

/*-----------------------------------------------------------------------------------*/


class KompasObjectHolder
{
protected:
    reference m_reference;
public:
    KompasObjectHolder() : m_reference(0) {}
    KompasObjectHolder(reference object_reference) : m_reference(object_reference) {}
    KompasObjectHolder(KompasObjectHolder& prev_holder) : m_reference(prev_holder.m_reference) { prev_holder.m_reference = 0; }
    KompasObjectHolder& operator =(KompasObjectHolder& prev_holder) {
        if (!m_reference) m_reference = prev_holder.m_reference, prev_holder.m_reference = 0; return *this; }
    ~KompasObjectHolder()
    {
        if (m_reference) DeleteObj(m_reference);
        m_reference = 0;
    }
    operator reference() { return m_reference; }
    operator bool() { return m_reference != 0; }
};


Я пишу вот так.


template <class T> 
T Replace_null(T &t) { T ret(t); t=T(); return ret; }

/* class KompasObjectHolder */

class KompasObjectHolder // Этот класс лучше превратить в шаблон 
 {
  protected:

   reference ptr; // лучше писать явно указатель на объект  KompasObject *

  public:

   // constructors

   KompasObjectHolder() : ptr(0) {}

   KompasObjectHolder(reference ptr_) : ptr(ptr_) {}

   KompasObjectHolder(KompasObjectHolder &obj) : ptr(obj.ptr) { obj.ptr=0; } 

   KompasObjectHolder(KompasObjectHolder &obj) : ptr(Replace_null(obj.ptr)) {} // Здесь следует использовать такую функцию

   KompasObjectHolder & operator = (KompasObjectHolder &obj) 
    {
     if( !ptr ) ptr = obj.ptr, obj.ptr = 0; 

     if( !ptr ) ptr=Replace_null(obj.ptr); // И здесь тоже
                                           // Кстати говоря, эта логика поведения мне не совсем понятна   

     return *this; // не экономь на строчках! 
    }

   ~KompasObjectHolder()
    {
     if( ptr ) DeleteObj(ptr); // Нормальная функция DeleteObj должна принимать нулевой указатель

     ptr=0; // Это не нужно
    }

   // methods

   operator reference() const { return ptr; } // здесь нужен const

   operator bool() const { return ptr!=0; } // и здесь тоже, более того, этот оператор не нужен и даже вреден, его функции будет выполнять первый оператор, будут проблемы с разрешением перегрузки
 };


Вот этот фрагмент порадовал.

        reference c_group = NewGroup(1); 
        EndGroup();
        SelectGroup(c_group, 2, 0,0,0,0);

        reference n_group = 0;
        if (ExistGroupObj(c_group))
        {
            KompasIteratorHolder contour_iterator(CreateIterator(ALL_OBJ, c_group));

            reference cur_obj_reference = MoveIterator(contour_iterator, 'F'); 
            while (cur_obj_reference)
            {
                int type = GetObjParam(cur_obj_reference, 0, 0, ALLPARAM);
                perimeter += ksGetCurvePerimeter (cur_obj_reference, ST_MIX_MM);
                cur_obj_reference = MoveIterator(contour_iterator, 'N');
            }
        }
        if (c_group) DeleteObj(c_group);


Если уж писать object holder, то следует их использовать. И методы им давать содержательные.

GroupHolder c_group(NewGroup(1));

....

reference cur_obj_reference = contour_iterator.move('F') ;

....


Вообщем, начинать надо с проработки базовых вещей. Проект сделан неряшливо. И я думаю, что вина здесь не только автора данного кода.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[3]: Оцените качество кода на С++
От: bnk СССР http://unmanagedvisio.com/
Дата: 18.09.14 19:18
Оценка: +1
Здравствуйте, GhostCoders, Вы писали:

GC>Здравствуйте, bnk, Вы писали:


bnk>>Код конечно не торт, но подобный ппц вполне ожидаем в продукте с N-летней историей.

GC> Этот код был написан в течении последних двух месяцев практически с нуля. Так сказать, свежачок....

Почерк динозавра просматривается
IMHO, ничего особо страшного, если не планируется этот код кому-либо отдавать или долго развивать-поддерживать, т.е. "выстрелил и забыл".
Re[2]: Оцените качество кода на С++
От: smeeld  
Дата: 18.09.14 19:23
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Вообщем, начинать надо с проработки базовых вещей. Проект сделан неряшливо. И я думаю, что вина здесь не только автора данного кода.


А что Вы скажете про код из опенсорсов?
Re[3]: Оцените качество кода на С++
От: zaufi Земля  
Дата: 18.09.14 19:35
Оценка:
Здравствуйте, -n1l-, Вы писали:

N>Здравствуйте, _Dreamer, Вы писали:


N>встроить линтер на прекоммит.


N>Так и называется, "Линтер"? В svn такое есть?


ну если погуглить "cpp lint", то один из первых находится Cpplint + еще что-то.
и да, оно легко прикручивается к svn, много всего настраивается (вкл\выкл) и в основном заточено на google style, который не всем нравится и мне в частности тоже, поэтому кучу фич нужно было выключать, и да, как и описано, довольно часто возникают false positive и приходится затыкать глупыми комментами // NOLINT.

как по мне, лучше тим лиду подписаться на коммиты и бегло просматривать их, выдавая люлей коммитерам нарушающим стиль. и параллельно с этим проводить code review, на которых мемберы учатся т.ч. правильному стилю.
Re: Оцените качество кода на С++
От: antropolog  
Дата: 18.09.14 20:08
Оценка:
Здравствуйте, GhostCoders, Вы писали:

я взглянул только на заголовочный файл и мои глаза закровоточили. Работа с графикой должна быть отделена от работы с юаем, т.е. должна идти работа с абстрактным графическим контекстом — хотя бы обёрткой над hbitmap, всё что должен делать диалог — обрабатывать ввод пользователя и звать методы этого контекста, в onPaint блитить к себе.
Re: Оцените качество кода на С++
От: vpchelko  
Дата: 18.09.14 20:30
Оценка: -1
Здравствуйте, GhostCoders, Вы писали:
Меня очень смущают "наполнители"
GC>reference CSewingDlg::DrawEllipse(reference element)
GC>{
GC>    EllipseParam epEllipseParam;
GC>    if (GetObjParam(element, &epEllipseParam, sizeof(EllipseParam), ALLPARAM))
GC>    {
 GC>        double x = epEllipseParam.xc,
GC>               y = epEllipseParam.yc;
GC>        double angle = epEllipseParam.ang;
GC>        double a = epEllipseParam.a;
GC>        double b = epEllipseParam.b;

GC>        const double perimeter =  ksGetCurvePerimeter (element, ST_MIX_MM); 

GC>        epEllipseParam.style = 1;

GC>        return ksEllipse(&epEllipseParam);
GC>    }
GC>    return 0;
GC>}

Таких "наполнителей" в коде дохрена.

А также куча сгенерированного кода. Свой и генерируемый код — нужно разделять, ибо поддержка IDE может перестанет работать. Я бы на вашем месте генерируемый код тут не постил.

Да и нотации типа m_, в новом коде я не приемлю, уже есть нормальные IDE.
Сало Украине, Героям Сала
Отредактировано 18.09.2014 20:31 vpchelko . Предыдущая версия .
Re[2]: Оцените качество кода на С++
От: antropolog  
Дата: 18.09.14 20:32
Оценка: +3
Здравствуйте, vpchelko, Вы писали:

V>Да и нотации типа m_, в новом коде я не приемлю, уже есть нормальные IDE.


помимо нормальных ide код может оказаться в блокноте, вьювере, письме, распечатан, передан по скайпу, запосчен на форум и стековерфлоу. Чем m_ помешал-то?
Re[2]: Оцените качество кода на С++
От: bnk СССР http://unmanagedvisio.com/
Дата: 18.09.14 20:45
Оценка:
Здравствуйте, vpchelko, Вы писали:

V>Здравствуйте, GhostCoders, Вы писали:


V>Меня очень смущают "наполнители"

V>
GC>>        y = epEllipseParam.yc;
GC>>        double angle = epEllipseParam.ang;
GC>>        double a = epEllipseParam.a;
GC>>        double b = epEllipseParam.b;
V>

V>Таких "наполнителей" в коде дохрена.

Наверное чтобы в дебаггере было удобно смотреть (возможно что в вотч эти самые x, y, a, b добавлены)
Хотя да, общая неряшливось присутствует. Явно у чувака с чувством прекрасного напряженка.

V>Да и нотации типа m_, в новом коде я не приемлю, уже есть нормальные IDE.


"m_" ничем не хуже "_" разве что на один символ длиннее, к тому же там mfc
Про поддержку IDE — она позволяет забыть про m_lpstr (венгерскую нотацию), но не про члены класса.
Re[3]: Оцените качество кода на С++
От: vpchelko  
Дата: 18.09.14 20:53
Оценка: -4
Здравствуйте, antropolog, Вы писали:

A>помимо нормальных ide код может оказаться в блокноте, вьювере, письме, распечатан, передан по скайпу, запосчен на форум и стековерфлоу.

Проблемы индейцев шерифа не волнуют.
A>Чем m_ помешал-то?
Потому-что номенклатура.
Сало Украине, Героям Сала
Re[3]: Оцените качество кода на С++
От: vpchelko  
Дата: 18.09.14 20:53
Оценка:
Здравствуйте, bnk, Вы писали:

bnk>Наверное чтобы в дебаггере было удобно смотреть (возможно что в вотч эти самые x, y, a, b добавлены)

bnk>Хотя да, общая неряшливось присутствует. Явно у чувака с чувством прекрасного напряженка.

Зато нам удобно смотреть — видим большую функцию на пол экрана — а по сути в ней ничего нет.

V>>Да и нотации типа m_, в новом коде я не приемлю, уже есть нормальные IDE.


bnk>"m_" ничем не хуже "_" разве что на один символ длиннее, к тому же там mfc

bnk>Про поддержку IDE — она позволяет забыть про m_lpstr (венгерскую нотацию), но не про члены класса.
ИМХО номенклатура. У меня локальные переменные и члены класса по разному подсвечиваются.
Сало Украине, Героям Сала
Отредактировано 18.09.2014 21:01 vpchelko . Предыдущая версия . Еще …
Отредактировано 18.09.2014 20:57 vpchelko . Предыдущая версия .
Re[4]: Оцените качество кода на С++
От: bnk СССР http://unmanagedvisio.com/
Дата: 18.09.14 21:00
Оценка:
Здравствуйте, vpchelko, Вы писали:

V> У меня локальные переменные и члены класса по разному подсвечиваются.


Тогда конечно можно и без "_". У меня одиниково. А как это в студии настроить?
Re[5]: Оцените качество кода на С++
От: vpchelko  
Дата: 18.09.14 21:23
Оценка: :)
Здравствуйте, bnk, Вы писали:

bnk>Тогда конечно можно и без "_". У меня одиниково. А как это в студии настроить?


Я использую VA для таких целей. Когда CLion — JetBrains IDE допилят до нормального состояния — про студию можно будет забыть.
Сало Украине, Героям Сала
Re[3]: Оцените качество кода на С++
От: Шахтер Интернет  
Дата: 18.09.14 21:30
Оценка:
Здравствуйте, smeeld, Вы писали:

S>Здравствуйте, Шахтер, Вы писали:


Ш>>Вообщем, начинать надо с проработки базовых вещей. Проект сделан неряшливо. И я думаю, что вина здесь не только автора данного кода.


S>А что Вы скажете про код из опенсорсов?


Это зависит от проекта, конечно же. Скажем boost сделан достаточно хорошо. Многие open source проекты, однако, сделаны довольно плохо. Скажем, в том же Linux е нет нормальной дисциплины имен, что для крупного C проекта просто необходимо.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re: Оцените качество кода на С++
От: RedUser Россия  
Дата: 18.09.14 21:34
Оценка:
Контору, кстати, в коде спалили
Re: Оцените качество кода на С++
От: RonWilson Россия  
Дата: 18.09.14 21:50
Оценка:
Здравствуйте, GhostCoders, Вы писали:

GC>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.


этого сотрудника не Александром случаем зовут?
Re[6]: Оцените качество кода на С++
От: bnk СССР http://unmanagedvisio.com/
Дата: 18.09.14 21:51
Оценка:
Здравствуйте, vpchelko, Вы писали:

V>Здравствуйте, bnk, Вы писали:


bnk>>Тогда конечно можно и без "_". У меня одиниково. А как это в студии настроить?


V>Я использую VA для таких целей.


Понятно что VA.
Я просто что-то не знаю у него такой настройки, чтобы переменные и мемберы показывать по-разному?
Отредактировано 18.09.2014 21:52 bnk . Предыдущая версия .
Re[4]: Оцените качество кода на С++
От: antropolog  
Дата: 18.09.14 22:05
Оценка:
Здравствуйте, vpchelko, Вы писали:

V>Проблемы индейцев шерифа не волнуют.

то что людям неудобно читать ваш код — это ваши проблемы, вообще-то.
Re[7]: Оцените качество кода на С++
От: antropolog  
Дата: 18.09.14 22:32
Оценка:
Здравствуйте, bnk, Вы писали:

bnk>Понятно что VA.

bnk>Я просто что-то не знаю у него такой настройки, чтобы переменные и мемберы показывать по-разному?

там в Fonts & Colors есть опция Local symbols in bold ( правда у меня достаточно старая версия, не знаю как в новых )
Re[8]: Оцените качество кода на С++
От: bnk СССР http://unmanagedvisio.com/
Дата: 18.09.14 22:39
Оценка:
Здравствуйте, antropolog, Вы писали:

A>Здравствуйте, bnk, Вы писали:


bnk>>Понятно что VA.

bnk>>Я просто что-то не знаю у него такой настройки, чтобы переменные и мемберы показывать по-разному?

A>там в Fonts & Colors есть опция Local symbols in bold ( правда у меня достаточно старая версия, не знаю как в новых )


А понял про что ты, пасиб. В новых так же. Просто тогда будет разный шрифт — мне это не нравится
Re[4]: Оцените качество кода на С++
От: Abyx Россия  
Дата: 18.09.14 22:51
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>>>У вас coding conventions вообще есть? где оформленный заголовок с именем файла, автором, датой создания, названием проекта?

A>>для всего этого есть система контроля версий.

L>Иногда, читай вероятность этого стремится к единице, информация об истории может потеряться. Происходит это при переезде на другую систему контроля версий или при просто копипасте файла из одного места в другое.


A>>есть смысл разве что писать копирайт, и то не очень-то нужно.


L>Заголовок с копирайтом позволяет сразу понять, что это, откуда оно взялось и кто в этом виноват. Даже когда работаешь из движущегося поезда без подключения к системе. Но я не настаиваю, хозяин-барин, это и правда не так уж и важно.


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

кажется что для максимальной продуктивности надо оптимизировать именно повседневную работу, а не те редкие случаи когда тебе пару раз в год надо поработать оффлайн или без VPN.

потери истории изменений и т.п. случаются, но такое бывает ну очень редко.

L>Комментарий должен быть. Он должен обосновывать необходимость этого холдера, в т.ч. содержать объяснение, почему аффтар не стал использовать умный указатель вместо этого, а также рассказывать, с какого такого бодуна объект вдруг стал называться ссылкой и зачем ему может понадобиться внешний удалятор. Любое инженерное решение должно быть обосновано. У меня сразу возникает вопрос — зачем нужно убивать объект, который является членом класса, неким внешним функтором. Если бы автор потрудился это описать в комментарии, он бы примерно на полпути, скорее всего, догадался бы, что инкапсуляция — не просто красивое слово, которое непонятно как правильно транслителировать на русском языке, а весьма правильный подход к проектированию подобных классов. Ну или бы четко описал, почему это должно быть сделано именно так, а не иначе.


это и есть умный указатель. код очевидно писался для С++03, по этому unique_ptr с кастомным делитером нету.
зачем используется функция DeleteObj — наверное это понятно из ее реализации. может там какой-то отладочный код, или возможность подключения глобального кастомного аллокатора.

и я совершенно не согласен с тем что любое инженерное решение должно быть обосновано комментариями.
многие паттерны и идиомы общеизвестны, легко распознаются на глаз, и потому в комментариях не нуждаются.

L>>>Все то же самое плюс — почему в одном заголовочном файле объявлено более одного класса?

A>>а почему бы и нет? особенно если классы используются вместе
L>В итоге все классы в программе используются вместе так или иначе. Это не повод всех их запихивать в один файл.

есть существенная разница между "запихнуть всё в 1 файл", и "запихнуть несколько в 1 файл".
требование "1 файл — 1 класс" звучит так же экстремально как и попытка запихнуть всю программу в 1 файл.
In Zen We Trust
Re[2]: Оцените качество кода на С++
От: GhostCoders Россия  
Дата: 18.09.14 22:52
Оценка:
Здравствуйте, RonWilson, Вы писали:

RW>этого сотрудника не Александром случаем зовут?

Нет, это Юра Лазарев.
Динозар еще тот. Он еще в 80-х на Фортране что-то писал.
Анархист и вообще, человек со странностями (не только в коде).

Оказывается, он про do{ }while(false) у МакКонела прочитал.

Бить функции на более мелкие — категорически отказывается, типа для него это плохо,
приходиться "скакать как козлу" с метода на метод, непонятным код становиться.

Заменить стиль линий (константа 1 в нескольких местах в этом коде) на const int TMP_STYLE_LINE = 1; согласился сделать только после часа дискуссий (!).
При этом мы в какие дебри только не лезли, он и в метафизику какую-то забрел и черт знает еще что (это когда я ему пытался доказать что магические константы — это зло).

При этом, он обвиняет меня в том, что я невнимательно прочитал "МакКонела" ("Совершенный код"),
а он прочитал его очень внимательно, результат прочтения данной книжки — это его выше приведенный код.

Я спрашиваю его — а что МакКонел рекомендует использовать магические константы и методы по 1000 строк в длину?
А он на какие-то неправильные аналогии выходит, например, "муж в квартире чинит свет. Летят стружки,
а жена его пилит. А муж в интересах жены делает". — поэтому у него в коде много балласта,
отладочной информации, которая портит форматирование и т.д.

Я совершенно серьезно, не шучу.

Вообще непробиваемый. С ним можно спорить и час и два и весь день. Толку почти 0.
Упрямый. Систему контроля версий (у нас GIT), не любит. Его перл это "это гит виноват".

Можно ему дать какую-нидубь книжку, например, "Рефакторинг" Мартина Фаулера — он ее прочтет, а делать все будет по-старому.

Могу составить список замечаний к его коду — только все равно, он его исправлять не будет.
Я уже махнул на него рукой — код ревью делать бессмысленно.
Поэтому он не работает в основной команде, а одиночкой над своим проектом.
Проект у него небольшой, в стиле "сделали" проект, сдали и забыли. Да и ничего там особо секретного нет, поэтому я выложил его тут как есть.
Никто туда лезть не собирается.

Может как-нибудь видео дискуссии с ним на видео сниму — круче Петросяна будет.

С ним спорить вообще невозможно — у меня некоторые сотрудники хватаются за голову.
Приемами демагогии владеет он отлично. Причем выходит так — что он один пишет отличный код,
"внимательно" прочитал "Совершенный Код" МакКонелла, а мы читали только поверхностно это книжку, и толком ее-то не знаем.

Еще типа что пишем одни только "обвертки", которые ничего не делают, много куча коротких функций,
которые тоже ничего не делают, только вызывают другие функции и т.д.
Третий Рим должен пасть!
Re[3]: Оцените качество кода на С++
От: niXman Ниоткуда https://github.com/niXman
Дата: 18.09.14 22:58
Оценка:
есть у меня такой знакомый.
так у меня когда с коллегами аргументы заканчивались — мы лезли на форум, задавали вопрос, и потом мнение большинства приводили как пример.
так он отвечал что-то типа "так они все идиоты, кого вы мне в пример приводите?"

в итоге, все забили ему что-либо объяснять...
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[3]: Оцените качество кода на С++
От: RonWilson Россия  
Дата: 18.09.14 23:06
Оценка:
Здравствуйте, GhostCoders, Вы писали:

RW>>этого сотрудника не Александром случаем зовут?

GC>Нет, это Юра Лазарев.

был у меня на оочень данвней работе подобный, читаю Ваш пост как про него почти. Единственное, что помогло вытащить как клеща наружу в реальный мир (не, не новомодные вещи какие-то) этого кренделя, так чуть подтолкнуть, сделать почву в собственных сомнениях, а потом он сам старался передовое отдать мне, а сам возвращался в свой погребок. Есть такие люди, с ними приходится работать. Плакать не надо, люди разные.
Re[5]: Оцените качество кода на С++
От: landerhigh Пират  
Дата: 19.09.14 05:21
Оценка: :)
Здравствуйте, Abyx, Вы писали:

A>тут есть два противоположных подхода -

A>ориентироваться на то что у тебя практически всегда есть самые современные инструменты, и использовать их,
A>или ориентироваться на то что однажды у тебя этих инструментов не будет, и по этому использовать эти замечательные инструменты по минимуму.

A>кажется что для максимальной продуктивности надо оптимизировать именно повседневную работу, а не те редкие случаи когда тебе пару раз в год надо поработать оффлайн или без VPN.


Мне офлайн работать приходится весьма регулярно.

A>потери истории изменений и т.п. случаются, но такое бывает ну очень редко.


Достаточно одного раза в 10 лет, и каждый раз придется заново проводить расследование "откуда вообще мы это взяли".

A>это и есть умный указатель. код очевидно писался для С++03, по этому unique_ptr с кастомным делитером нету.


Каким местом это умный указатель? Ты повнимательнее посмотри. Если это и умный указатель, то кривой на всю голову.
И еще, сегодня 2014, С++03 совершенно неочевиден.

A>зачем используется функция DeleteObj — наверное это понятно из ее реализации. может там какой-то отладочный код, или возможность подключения глобального кастомного аллокатора.


Еще раз, DeleteObj применяется к значению, а не указателю. Это либо самый страшный случай нарушения принципов ООП, который я видел в этом году, либо что-то чрезвычайно хитрое. В обоих случаях такое решение должно быть обосновано, и обосновано именно здесь.

A>и я совершенно не согласен с тем что любое инженерное решение должно быть обосновано комментариями.


Ты можешь не соглашаться, но это не делает тебя правым. Я требую обосновывать абсолютно все классы. Без исключения.
В частности и особенно те, которые очень похожи на что-то стандартное.
И знаешь что? Во многих случаях аффтар подобного говнокода, со скрипом рожая камент (каменты писать некогда, копать нужно), где-то на третьей строчке понимает, что написал какую-то фигню, и что все это можно заменить стандартным(бустовским) shared_ptr, например.

A>многие паттерны и идиомы общеизвестны, легко распознаются на глаз, и потому в комментариях не нуждаются.


Тут распознается лишь один паттерн.. и это вовсе не хорошие новости.

L>>>>Все то же самое плюс — почему в одном заголовочном файле объявлено более одного класса?

A>>>а почему бы и нет? особенно если классы используются вместе
L>>В итоге все классы в программе используются вместе так или иначе. Это не повод всех их запихивать в один файл.

A>есть существенная разница между "запихнуть всё в 1 файл", и "запихнуть несколько в 1 файл".

A>требование "1 файл — 1 класс" звучит так же экстремально как и попытка запихнуть всю программу в 1 файл.

В любом случае здесь страшная мешанина.
www.blinnov.com
Re[3]: Оцените качество кода на С++
От: Константин Россия  
Дата: 19.09.14 06:18
Оценка:
Здравствуйте, GhostCoders, Вы писали:

GC>Нет, это ***.

GC>При этом, он обвиняет меня в том, что ..., Вообще непробиваемый ... Упрямый ...
GC>Я уже махнул на него рукой ... Поэтому он не работает в основной команде, а одиночкой над своим проектом
GC>С ним спорить вообще невозможно — у меня некоторые сотрудники хватаются за голову. Приемами демагогии владеет он отлично ...
GC>Еще типа что пишем одни только "обвертки", которые ничего не делают...

Как-то не комильфо... Раскрываете имена, да и не о C++ совсем. Если всё так плохо, почему он ещё работает в компании?

GC> Бить функции на более мелкие — категорически отказывается, типа для него это плохо, приходиться "скакать как козлу" с метода на метод, непонятным код становиться. Заменить стиль линий (константа 1 в нескольких местах в этом коде) на const int TMP_STYLE_LINE = 1; согласился сделать только после часа дискуссий (!).


Спорить о таких вещах странно: не стоит тратить нервную энергию на то, что можно описать в корпоративном стандарте кодирования..
Re[3]: Оцените качество кода на С++
От: uzhas Ниоткуда  
Дата: 19.09.14 07:51
Оценка:
Здравствуйте, GhostCoders, Вы писали:

GC>Оказывается, он про do{ }while(false) у МакКонела прочитал.


в целом, я положительного мнения об этом труде
решил найти подтверждения:
в главе 16 Циклы можно найти параграф "Аномальные циклы с выходом", где демонстрируется замена "плохой практики (С++)"
goto Start:
while ( expression ) {
  // Делаем что-то.
  ...

  Start:

  // Делаем что-то еще.
  ...
}


на православный лучший вариант
while ( true) {
  Блоки перед и после break поменяны местами
  // Делаем что-то еще.
  ...

  if ( !( expression ) ) {
    break;
  }

  // Делаем что-то.
  ...
}

то есть показано, как заменить цикл со странным входом на обычный цикл с break выходом. подчеркну, что здесь описываются циклы (то есть когда подразумевается наличие нескольких итераций)

в следующей же главе обсуждается goto и не упоминается применение "аномальных" циклов, как альтернативы. даже название уже намекает, что использовать их нежелательно
Re[3]: Оцените качество кода на С++
От: _Dreamer Россия  
Дата: 19.09.14 08:11
Оценка:
Здравствуйте, -n1l-, Вы писали:

N>Здравствуйте, _Dreamer, Вы писали:


N>встроить линтер на прекоммит.


N>Так и называется, "Линтер"? В svn такое есть?


Уже сказали про cpplint, по факту это может быть что угодно для проверки стиля.
Хоть clang-format (http://clang.llvm.org/docs/ClangFormatStyleOptions.html)
У cpplint действительно есть false-positive срабатывания, но с этим можно жить, а можно его допиливать.
Так что это можно прикрутить не только на svn.
Re[4]: Оцените качество кода на С++
От: _Dreamer Россия  
Дата: 19.09.14 08:16
Оценка: +1
Здравствуйте, zaufi, Вы писали:

Z>как по мне, лучше тим лиду подписаться на коммиты и бегло просматривать их, выдавая люлей коммитерам нарушающим стиль. и параллельно с этим проводить code review, на которых мемберы учатся т.ч. правильному стилю.


Это может быть куча времени, к сожалению. А скрипт не устает и не отвлекается, как раз для него работа.
К тому же, Вы предлагаете оценивать коммиты постфактум?
Именно этого я бы и не стал допускать.
Потому что по разным причинам коммит может так и остаться в ненадлежащем виде.
А вот тупо не дать закоммитить без -n уже гораздо действеннее, не смотря на некоторые неудобства.
Re[13]: Оцените качество кода на С++
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 19.09.14 08:38
Оценка:
Здравствуйте, smeeld, Вы писали:

S>И вызов __Unwind_RaiseException произойдёт при всяком throw, ибо как есть стандарт.

Речь как бы не совсем про это была.
Sic luceat lux!
Re[3]: Оцените качество кода на С++
От: SleepyDrago Украина  
Дата: 19.09.14 09:26
Оценка:
Здравствуйте, GhostCoders, Вы писали:

GC>Вообще непробиваемый. С ним можно спорить и час и два и весь день. Толку почти 0.

GC>Упрямый. Систему контроля версий (у нас GIT), не любит. Его перл это "это гит виноват".

вина лида очевидна. Не позднее 4го дня затихшего сотрудника надо заставить систематизировать то, что он делает. "2 месяца" это фейспалм.
Как там было "Поросеночек рос, рос и выросло ... Что выросло, то выросло!". Тем более когда человек нуждается в помощи по наведению порядка.

В нынешнем виде этот код обуза для проекта и тянет его ко дну. Включать в билд можно только если экономика требует. Иначе в папочку "музей".
Re[14]: Оцените качество кода на С++
От: smeeld  
Дата: 19.09.14 09:49
Оценка:
Здравствуйте, Kernan, Вы писали:

K>Речь как бы не совсем про это была.


Короче, вместо try{ throw }catch() используем старый добрый do{ break; while(0)
так правильней.
Re[4]: Оцените качество кода на С++
От: DarkEld3r  
Дата: 19.09.14 10:10
Оценка: +2
Здравствуйте, landerhigh, Вы писали:

L>Заголовок с копирайтом позволяет сразу понять, что это, откуда оно взялось и кто в этом виноват. Даже когда работаешь из движущегося поезда без подключения к системе.

Работал однажды над легаси проектом. Там такое было принято. Причём когда файл кто-то редактировал, то про коммите изменений в шапку дописывалось примерно следующее:
<номер изменения> <автор> <сообщение> <дата>

Ну и что оно помогает понять? Особенно когда количество изменений переваливает за сотню (так и было), а кода который люди добавляли в файле уже и не осталось. Без системы контроля версий это просто нагромождение бессмысленной "информации".

К счастью, в какой-то момент от этого решили избавиться.

L>Я знаю. Это не отменяет того, что хорошей инженерной практикой является комментирование подобного. Даже если это всем известно, это не перестает быть грязным хаком.

Не уверен. А то так можно далеко зайти. Скажем, "pragma once" стандартом так и не стала. Тем не менее, (практически) все используют и сомневаюсь, что комментируют. А что? Тоже своего рода "нестандартный хак".
Re[5]: Оцените качество кода на С++
От: vpchelko  
Дата: 19.09.14 10:43
Оценка:
Здравствуйте, antropolog, Вы писали:

A>то что людям неудобно читать ваш код — это ваши проблемы, вообще-то.


настоящему индейцу...
Сало Украине, Героям Сала
Отредактировано 19.09.2014 10:47 vpchelko . Предыдущая версия .
Re[2]: Оцените качество кода на С++
От: Abyx Россия  
Дата: 19.09.14 10:49
Оценка: +5
Здравствуйте, vpchelko, Вы писали:

V>Да и нотации типа m_, в новом коде я не приемлю, уже есть нормальные IDE.

m_ пишут не вместо подсветки, а чтобы не писать this-> если есть параметр или локальная переменная с тем же именем.
struct Foo {
  int x;

  void f(int x) {
    this->x = x;
  }

  void g() {
    ...
    int x = ...
    ...
    this->x = x;
  }
};
In Zen We Trust
Re[15]: Оцените качество кода на С++
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 19.09.14 11:09
Оценка:
Здравствуйте, smeeld, Вы писали:

S>Здравствуйте, Kernan, Вы писали:


K>>Речь как бы не совсем про это была.


S>Короче, вместо try{ throw }catch() используем старый добрый do{ break; while(0)

S>так правильней.
Нет, правильнее выделить сущности и использовать RAII, try-catch там вообще может быть не нужен. Если бы там были только математические вычисления то подход с do-break имел бы смысл.
Sic luceat lux!
Re: Оцените качество кода на С++
От: B0FEE664  
Дата: 19.09.14 14:06
Оценка:
Здравствуйте, GhostCoders, Вы писали:

GC>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.


О, да! Руку мастера сразу видно ! Особенно тернарный оператор нравится (что? switch? enum? ну не надо усложнять такой компактный код!):
GC>
GC>        Equidistant_curve->BaseObject = idoDrawingContour;
GC>        Equidistant_curve->CutMode = equiparam->cutMode == 0? FALSE : TRUE;
GC>        Equidistant_curve->LeftRadius = equiparam->radLeft;
GC>        Equidistant_curve->RightRadius = equiparam->radRight;
GC>        Equidistant_curve->Side = equiparam->side == 2? ksETBoth :
GC>            equiparam->side == 0? ksETLeft : equiparam->side == 1? ksETRight : ksETUnknown;
GC>        Equidistant_curve->Style = equiparam->style;        // = ksCSConstruction; // = 6

...

GC>                Equid_curve->BaseObject = idoObject;
GC>                Equid_curve->CutMode = equiparam->cutMode == 0? FALSE : TRUE;
GC>                Equid_curve->LeftRadius = equiparam->radLeft;
GC>                Equid_curve->RightRadius = equiparam->radRight;
GC>                Equid_curve->Side = equiparam->side == 2? ksETBoth :
GC>                    equiparam->side == 0? ksETLeft : equiparam->side == 1? ksETRight : ksETUnknown;
GC>                Equid_curve->Style = equiparam->style;        // = ksCSConstruction; // = 6

...


GC>                    Equid_curve->BaseObject = idoObject;
GC>                    Equid_curve->CutMode = equiparam->cutMode == 0? FALSE : TRUE;
GC>                    Equid_curve->LeftRadius = equiparam->radLeft;
GC>                    Equid_curve->RightRadius = equiparam->radRight;
GC>                    Equid_curve->Side = equiparam->side == 2? ksETBoth :
GC>                        equiparam->side == 0? ksETLeft : equiparam->side == 1? ksETRight : ksETUnknown;
GC>                    Equid_curve->Style = equiparam->style;        // = ksCSConstruction; // = 6

...

GC>                Equidistant_curve->BaseObject = drawing_object;
GC>                Equidistant_curve->CutMode = equidParam.cutMode == 0? FALSE : TRUE;
GC>                Equidistant_curve->DegenerateSegment = equidParam.degState == 0? FALSE : TRUE;
GC>                Equidistant_curve->LeftRadius = equidParam.radLeft;
GC>                Equidistant_curve->RightRadius = equidParam.radRight;
GC>                Equidistant_curve->Side = equidParam.side == 2? ksETBoth :
GC>                    equidParam.side == 0? ksETLeft : equidParam.side == 1? ksETRight : ksETUnknown;
GC>                Equidistant_curve->Style = equidParam.style;

GC>


Следующий кусок кода просто гениален!
Я называю его "Мастер даёт дегенеративному сегменту второй шанс":

    BOOL isok = false;
    BOOL test_isok = false;
    Equidistant_curve->DegenerateSegment = equiparam->degState == 0? FALSE : TRUE;
    do {
        Equidistant_curve->BaseObject = idoDrawingContour;
        Equidistant_curve->CutMode = equiparam->cutMode == 0? FALSE : TRUE;
        Equidistant_curve->LeftRadius = equiparam->radLeft;
        Equidistant_curve->RightRadius = equiparam->radRight;
        Equidistant_curve->Side = equiparam->side == 2? ksETBoth :
            equiparam->side == 0? ksETLeft : equiparam->side == 1? ksETRight : ksETUnknown;
        Equidistant_curve->Style = equiparam->style;        // = ksCSConstruction; // = 6

        isok = Equidistant_curve->Update();

        if (!isok) {
            Equidistant_curve->DegenerateSegment = TRUE;
            //Equidistant_curve->CutMode = TRUE;
        }
        isok = isok || test_isok;
        test_isok = true;
    } while (!isok);


Не, ну правда, вполне толерантный код! И, главное, скучать не придётся!
И каждый день — без права на ошибку...
Re[5]: Оцените качество кода на С++
От: landerhigh Пират  
Дата: 19.09.14 14:12
Оценка: +1
Здравствуйте, DarkEld3r, Вы писали:


DE>Работал однажды над легаси проектом. Там такое было принято. Причём когда файл кто-то редактировал, то про коммите изменений в шапку дописывалось примерно следующее:

DE><номер изменения> <автор> <сообщение> <дата>

У нас тоже такое было. Это отросло из старых проектов, когда VCS либо не было, либо были в зачаточном состоянии.

DE>Ну и что оно помогает понять? Особенно когда количество изменений переваливает за сотню (так и было), а кода который люди добавляли в файле уже и не осталось. Без системы контроля версий это просто нагромождение бессмысленной "информации".


Шапка помогает отследить происхождение файла. Кто, когда, какой проект. Сразу понятно, есть ли смысл искать аффтара.
Нередко бывает, кто какой-то файл копируется из проекта в проект, во внтутренние тулзы, откуда он опять попадает в продакшен и т.п.

DE>К счастью, в какой-то момент от этого решили избавиться.


Наши тоже прекратили ручное отслеживание истории при переходе на VCS.

L>>Я знаю. Это не отменяет того, что хорошей инженерной практикой является комментирование подобного. Даже если это всем известно, это не перестает быть грязным хаком.

DE>Не уверен. А то так можно далеко зайти. Скажем, "pragma once" стандартом так и не стала. Тем не менее, (практически) все используют и сомневаюсь, что комментируют. А что? Тоже своего рода "нестандартный хак".

Вещи вроде
#pragma once

обычно относят в coding conventions, где описывается, используются ли guard macro или эта прагма.
www.blinnov.com
Re[6]: Оцените качество кода на С++
От: uzhas Ниоткуда  
Дата: 19.09.14 15:11
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>У нас тоже такое было. Это отросло из старых проектов, когда VCS либо не было, либо были в зачаточном состоянии.


лично мне недостает (ибо на текущем месте никто не использует) той шапки, где VCS автоматом прописывает дату коммита, автора и ревизию (как тут: https://www.cs.utah.edu/dept/old/texinfo/cvs/cvs_16.html ). все же помогало ориентироваться в проекте и выявлять случаи, когда "ой, забыл твой фикс затащить" или "кто эту тут пошевелил код, вчера было по-другому": по шапке можно быстро понять неактуальность файла. внешние тулзы все же более неповоротливы. минусом только было сложность анализа диффа файлов для одного проекта в разных бранчах сторонними утилитами типа Araxis — дифф показывался для всех файлов из-за разницы в шапке.
Re: (ключница) Си-шник писал?
От: Basil2 Россия https://starostin.msk.ru
Дата: 19.09.14 15:54
Оценка: +1
Здравствуйте, GhostCoders, Вы писали:

GC>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.


Обычно именно сишники выдают такой С++ код. Или драйверисты.

В общем случае не лечится. Их период воспитывания вкуса пришелся на другую эпоху, для них подобный код действительно неплох. Можно дать им книжку типа Фаулера ("Рефакторинг") и надеятся, что привьются правильные подходы. Но не факт...
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Re[3]: Оцените качество кода на С++
От: omgOnoz  
Дата: 19.09.14 16:47
Оценка:
Здравствуйте, Abyx, Вы писали:

A>m_ пишут не вместо подсветки, а чтобы не писать this-> если есть параметр или локальная переменная с тем же именем.

A>
A>struct Foo {
A>  int x;

A>  void f(int x) {
    this->>x = x;
A>  }

A>  void g() {
A>    ...
A>    int x = ...
A>    ...
    this->>x = x;
A>  }
A>};
A>


Я в таких случаях за использование this->x = x;
К тому же я еще пишу много на java — там такой стиль обще принят. Например автогенерация кода для getter/setter и конструкторов инициализирующих поля объекта.
Мне например очень удобно, когда код смотрится одинаково на C++ и на Java — да и других языках. Да и в принципе мне все равно какой язык использовать. Но эталон кодинг стайла — пока что Java.
Отредактировано 19.09.2014 17:01 omgOnoz . Предыдущая версия . Еще …
Отредактировано 19.09.2014 17:01 omgOnoz . Предыдущая версия .
Отредактировано 19.09.2014 16:57 omgOnoz . Предыдущая версия .
Отредактировано 19.09.2014 16:54 omgOnoz . Предыдущая версия .
Отредактировано 19.09.2014 16:48 omgOnoz . Предыдущая версия .
Re: Оцените качество кода на С++
От: Философ Ад http://vk.com/id10256428
Дата: 19.09.14 17:03
Оценка: +1
код едва вытягивает на оценку "удовлетворительно", но шанс у этого кода есть — его исправить можно.

недостатки обсосали выше, но особенно "порадовало" использование тернарных операторов вкупе с плохим форматированием — такой код читать тяжко.
огромные методы, которые не понятно что делают, и которые можно читать с перекурами вызывают желание основательно отрефакторить этот код.
Всё сказанное выше — личное мнение, если не указано обратное.
Re[4]: Оцените качество кода на С++
От: Abyx Россия  
Дата: 19.09.14 18:38
Оценка:
Здравствуйте, omgOnoz, Вы писали:

A>>m_ пишут не вместо подсветки, а чтобы не писать this-> если есть параметр или локальная переменная с тем же именем.

A>>
A>>  void f(int x) {
A>>    this->x = x;
A>>  }
A>>

O>Я в таких случаях за использование this->x = x;

и если ты забудешь написать this-> то получишь трудно отлавливаемый баг.
In Zen We Trust
Re[5]: Оцените качество кода на С++
От: omgOnoz  
Дата: 19.09.14 18:43
Оценка:
Здравствуйте, Abyx, Вы писали:

A>и если ты забудешь написать this-> то получишь трудно отлавливаемый баг.


Вот это не правда.

Компилятор и IDE ругаются в таком случае.

Хотя есть настоящие индейцы, которым все нипочем. Редактируют код в блокноте — собирают сразу релиз, игнорируют предупреждения....
Отредактировано 19.09.2014 18:47 omgOnoz . Предыдущая версия . Еще …
Отредактировано 19.09.2014 18:46 omgOnoz . Предыдущая версия .
Re[3]: Оцените качество кода на С++
От: Zhendos  
Дата: 19.09.14 21:42
Оценка: +1
Здравствуйте, MTD, Вы писали:

MTD>Здравствуйте, Andrew.W Worobow, Вы писали:


AWW>>мало коментариев.


MTD>В хорошем коде комментариев быть не должно.


Не согласен,
например мы реализовали какой-то сложный математический
метод, нужен хотя бы комментарий, типа метод такой-то, см. статью такую-то,
а лучше бы еще объяснение почему выбран именно метод (хотя это можно и в документацию
засунуть).

Иными словами как бы хорош код не был, всегда существуют такие алгоритмы,
которые сложно понять имея только код.
Re[6]: Оцените качество кода на С++
От: Zhendos  
Дата: 19.09.14 21:46
Оценка:
Здравствуйте, landerhigh, Вы писали:


A>>потери истории изменений и т.п. случаются, но такое бывает ну очень редко.


L>Достаточно одного раза в 10 лет, и каждый раз придется заново проводить расследование "откуда вообще мы это взяли".


Бэкапы делать пробовали?
Re[7]: Оцените качество кода на С++
От: landerhigh Пират  
Дата: 19.09.14 22:47
Оценка:
Здравствуйте, Zhendos, Вы писали:

A>>>потери истории изменений и т.п. случаются, но такое бывает ну очень редко.


L>>Достаточно одного раза в 10 лет, и каждый раз придется заново проводить расследование "откуда вообще мы это взяли".


Z>Бэкапы делать пробовали?


А подумать, что означает "потеря истории изменений", как она происходит и как и когда обнаруживается, и каким боком тут бекапы, подумать не пробовали, прежде чем написать?
www.blinnov.com
Re[4]: Оцените качество кода на С++
От: MTD https://github.com/mtrempoltsev
Дата: 20.09.14 04:21
Оценка:
Здравствуйте, Zhendos, Вы писали:

Z>например мы реализовали какой-то сложный математический

Z>метод, нужен хотя бы комментарий, типа метод такой-то, см. статью такую-то,

Это единственный случай когда в хорошем коде комментарий уместен.
Re[5]: Оцените качество кода на С++
От: Хон Гиль Дон Россия  
Дата: 20.09.14 08:14
Оценка: +3
Здравствуйте, MTD, Вы писали:

Z>>например мы реализовали какой-то сложный математический

Z>>метод, нужен хотя бы комментарий, типа метод такой-то, см. статью такую-то,

MTD>Это единственный случай когда в хорошем коде комментарий уместен.


Не, далеко не единственный. Есть куча ситуаций, где недостаточно внимательный разработчик может нарваться на неприятности. Я, например, всегда комментирую "проваливающиеся" (без break) ветки в switch, или места, где важен порядок объявления членов класса. Со сторонними библиотеками тоже всяких приколов хватает. Вообще, нарвался на неожиданность — прокомментируй. Не понял чего-то быстро (засомневался, а все ли тут делается корректно и вынужден перепроверять, к примеру) в своем же коде через год после написания — прокомментируй, по свежим впечатлениям.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[5]: Комментарии
От: Qbit86 Кипр
Дата: 20.09.14 09:57
Оценка: +6 :)
Здравствуйте, MTD, Вы писали:

MTD>Это единственный случай когда в хорошем коде комментарий уместен.


"Хороший код" иллюстрирует максимум только то, что он делает. Он не может описать того, что он не делает, и почему он этого — ещё или уже — не делает.

Всегда есть множество вариантов сделать одно и то же. И простое предъявление одного из вариантов не говорит о том, почему именно ему отдано предпочтение перед другими. Планируется какой-то рефакторинг, учтены какие-то ограничения одного из компиляторов, воркэраунд вокруг недокументированного поведения сторннего API, etc, etc, etc. "В хорошем коде комментариев быть не должно" — это весьма популярный, яркий, лаконичный, ёмкий и совершенно неправильный принцип. Используется в основном теоретиками и прочими гуманитариями.
Глаза у меня добрые, но рубашка — смирительная!
Re[6]: Комментарии
От: MTD https://github.com/mtrempoltsev
Дата: 20.09.14 12:31
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>"Хороший код" иллюстрирует максимум только то, что он делает. Он не может описать того, что он не делает, и почему он этого — ещё или уже — не делает.


Действительно, почему функция OpenFile не форматирует диск? Непорядок, надо фичу добавить, но аккуратно задокументировать!

Q>Используется в основном теоретиками и прочими гуманитариями.


А обвинения в увлечении теорией, по моему опыту, звучат в основном от любителей трясти, а не думать.
Re[6]: Оцените качество кода на С++
От: MTD https://github.com/mtrempoltsev
Дата: 20.09.14 12:34
Оценка:
Здравствуйте, Хон Гиль Дон, Вы писали:

ХГД>Есть куча ситуаций, где недостаточно внимательный разработчик может нарваться на неприятности. Я, например, всегда комментирую "проваливающиеся" (без break) ветки в switch, или места, где важен порядок объявления членов класса.


Перепиши код проще, чтобы недостаточно внимательный разработчик не нарвался — гарантии, что он прочтет твой комментарий нет.

ХГД>Со сторонними библиотеками тоже всяких приколов хватает. Вообще, нарвался на неожиданность — прокомментируй.


Бывает, но мы же о хорошем коде фантазируем.
Re[7]: OpenFile
От: Qbit86 Кипр
Дата: 20.09.14 12:46
Оценка: :)
Здравствуйте, MTD, Вы писали:

Q>>"Хороший код" иллюстрирует максимум только то, что он делает. Он не может описать того, что он не делает, и почему он этого — ещё или уже — не делает.

MTD>Действительно, почему функция OpenFile не форматирует диск? Непорядок, надо фичу добавить, но аккуратно задокументировать!

OpenFile? Что ещё за OpenFile? Почему в этом месте разраюотчик решил открыть какой-то файл? Этот код может же выполняться на мобильной платформе, где нельзя просто так взять, и открыть файл. Или не будет выполняться? Зачем открывать какие-то файлы, если в любом языке и библиотеке есть примитивы для работы с XML или другими форматами данных? Я программировал на C++ и C# под десктоп, iOS и Android. И везде OpenFile почти наверняка вызвал бы вопросы.

MTD>А обвинения в увлечении теорией, по моему опыту, звучат в основном от любителей трясти, а не думать.


Если человек не может вообразить примеры, когда полезны комментарии в коде — значит, он чужой код не читает. Может, он джедай-одиночка; свой-то код, понятно, "всегд хороший и не нуждается в комментариях".
Глаза у меня добрые, но рубашка — смирительная!
Re[6]: Оцените качество кода на С++
От: bnk СССР http://unmanagedvisio.com/
Дата: 20.09.14 12:58
Оценка:
Здравствуйте, Хон Гиль Дон, Вы писали:

ХГД> Я, например, всегда комментирую "проваливающиеся" (без break) ветки в switch, или места, где важен порядок объявления членов класса.


Ага, классный принцип — закладывать мины и обставлять их флажками
Re[5]: Оцените качество кода на С++
От: bnk СССР http://unmanagedvisio.com/
Дата: 20.09.14 12:59
Оценка: +3
Здравствуйте, MTD, Вы писали:

Z>>например мы реализовали какой-то сложный математический

Z>>метод, нужен хотя бы комментарий, типа метод такой-то, см. статью такую-то,

MTD>Это единственный случай когда в хорошем коде комментарий уместен.


Не совсем. IMHO, при развитии/поддержке очень полезны комментарии, отвечающе не на вопрос "что делает код",
а на вопрос "почему/зачем это делается" или "почему это делается именно так, а не иначе".

В приведенном выше коде этим увы и не пахнет
Re[7]: Оцените качество кода на С++
От: aik Австралия  
Дата: 20.09.14 13:50
Оценка: -1
Здравствуйте, uzhas, Вы писали:

U>эта "идиома" в плюсовом коде не нужна. в старом добром Си — на здоровье


эта идиома в старом добром си делается через goto на порядок прямее.
Re: Оцените качество кода на С++
От: aik Австралия  
Дата: 20.09.14 14:02
Оценка: +1
Здравствуйте, GhostCoders, Вы писали:

GC>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.


лютый трындец. write-only. надо переделать чтоб был единый code design style — префиксы переменных, какие отступы у скопов и так далее, комментарии должны быть комментариями, а не отладочным выводом и функции типа equal_values не должны иметь параметров, которые не используются никогда.
Re[8]: OpenFile
От: MTD https://github.com/mtrempoltsev
Дата: 20.09.14 14:45
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>OpenFile?


ОткрытьФайл

Q>Что ещё за OpenFile?


Функция которая открывает файл.

Q>Почему в этом месте разраюотчик решил открыть какой-то файл?


Действительно, зачем в методе ReadConfigFromFile (ПрочестьКонфигурациюИзФайла) разработчику нужно открыть файл? Непонятно! Надо чтобы диск форматировался и само-собой оставить об этом комментарий.

Q>Этот код может же выполняться на мобильной платформе


Придумай что-нибудь оригинальней, передергивания стали скучными.

Q>Если человек не может вообразить примеры, когда полезны комментарии в коде — значит, он чужой код не читает. Может, он джедай-одиночка; свой-то код, понятно, "всегд хороший и не нуждается в комментариях".


Я читал очень много кода, был код который читался замечательно без комментариев, было много говнокода "отлично" прокомментированного:

file f = open("xxx") // Открываем файл
x = read(f, 5) // Читаем из файла 5 байт

А еще, попадался код с историей в десяток лет, где изначально осмысленные комментарии устаревали и порой вообще вводили в заблуждение.
Re[7]: Комментарии
От: landerhigh Пират  
Дата: 20.09.14 15:26
Оценка: 6 (1) +1
Здравствуйте, MTD, Вы писали:

Q>>"Хороший код" иллюстрирует максимум только то, что он делает. Он не может описать того, что он не делает, и почему он этого — ещё или уже — не делает.


MTD>Действительно, почему функция OpenFile не форматирует диск? Непорядок, надо фичу добавить, но аккуратно задокументировать!


OpenFile — очень хороший пример функции, документирование которой строго обязательно. Описание, какие режимы можно использовать, на каких платформах (если поддерживает больше одной) и файловых системах и как они будут работать, а также прямое описание ошибочных ситуаций.

Более того, во многих случаях необходимо документировать также и использование оной функции. Чтобы указать, почему используется именно этот режим открытия, или почему тут совершенно необходимо использование юникодной версии и префикса "\\?\" и т.д.
www.blinnov.com
Re[8]: Комментарии
От: niXman Ниоткуда https://github.com/niXman
Дата: 20.09.14 15:30
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>OpenFile — очень хороший пример функции, документирование которой строго обязательно. Описание, какие режимы можно использовать, на каких платформах (если поддерживает больше одной) и файловых системах и как они будут работать, а также прямое описание ошибочных ситуаций.


L>Более того, во многих случаях необходимо документировать также и использование оной функции. Чтобы указать, почему используется именно этот режим открытия, или почему тут совершенно необходимо использование юникодной версии и префикса "\\?\" и т.д.


это какая-то утопия, имхо =)
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[9]: Комментарии
От: landerhigh Пират  
Дата: 20.09.14 15:36
Оценка: +1 :)
Здравствуйте, niXman, Вы писали:

L>>Более того, во многих случаях необходимо документировать также и использование оной функции. Чтобы указать, почему используется именно этот режим открытия, или почему тут совершенно необходимо использование юникодной версии и префикса "\\?\" и т.д.


X>это какая-то утопия, имхо =)


Отнюдь. Харрасмент, буллинг и геноцид, ковровые бомбардировки и обыкновенный фашизм во время код ревью творят живительные чудеса. Через полгода подопытные (если не уволились раньше) даже перестают воспринимать критику кода как личное оскорбление.
www.blinnov.com
Re[10]: Комментарии
От: niXman Ниоткуда https://github.com/niXman
Дата: 20.09.14 15:39
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Отнюдь. Харрасмент, буллинг и геноцид, ковровые бомбардировки и обыкновенный фашизм во время код ревью творят живительные чудеса. Через полгода подопытные (если не уволились раньше) даже перестают воспринимать критику кода как личное оскорбление.


я согласен с тем, что такой код хорошо и приятно поддерживать. но я сомневаюсь что такое возможно в нашей-то реальности
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[11]: Комментарии
От: landerhigh Пират  
Дата: 20.09.14 15:44
Оценка:
Здравствуйте, niXman, Вы писали:

X>я согласен с тем, что такой код хорошо и приятно поддерживать. но я сомневаюсь что такое возможно в нашей-то реальности


К совершенству надо стремиться. Даже если оно недостижимо. Иначе бренность бытия поглотит и само существование станет бессмысленным
www.blinnov.com
Re[12]: Комментарии
От: niXman Ниоткуда https://github.com/niXman
Дата: 20.09.14 15:45
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>К совершенству надо стремиться. Даже если оно недостижимо. Иначе бренность бытия поглотит и само существование станет бессмысленным

да. об этом я совсем забыл =)
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[10]: Комментарии
От: RonWilson Россия  
Дата: 20.09.14 15:46
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Отнюдь. Харрасмент, буллинг и геноцид, ковровые бомбардировки и обыкновенный фашизм во время код ревью творят живительные чудеса. Через полгода подопытные (если не уволились раньше) даже перестают воспринимать критику кода как личное оскорбление.


расскажите практические примеры, у меня фантазия закончилась на угрозах семье, но код стал реально чище
Re[8]: Комментарии
От: MTD https://github.com/mtrempoltsev
Дата: 20.09.14 15:48
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>OpenFile — очень хороший пример функции, документирование которой строго обязательно. Описание, какие режимы можно использовать


Хороший код сам является в значительной мере документацией. Можно обозвать метод open и сказать, что он открывает вообще все, детали смотрите в документации. А можно сделать классы File, TcpSocket, SerialPort с методами Open. Затем, метод Open класса File может две строки — путь к файлу и режим открытия, тут без документации не разобраться. А может принимать enum Mode { ReadOnly, WriteOnly, ... } и объект типа FilePath, который представляет собой путь к файлу.

L>на каких платформах (если поддерживает больше одной)


Сова затрещала. Нормальная библиотека имеет список поддерживаемых платформ — вот ответ. Библиотека которая ведет себя по разному на разных платформах отправляется в топку.

L>и файловых системах и как они будут работать


Ага и на каких дисках, например WD-only, тебе же голову больше занять нечем. Файл — понятие высокоуровневое, забивать себе голову файловыми системами, секторами и блоками как-то неадекватно.

L>а также прямое описание ошибочных ситуаций.


Это да. Только речь то шла о комментариях, документирование отдельная большая тема.
Re[9]: Комментарии
От: landerhigh Пират  
Дата: 20.09.14 16:20
Оценка:
Здравствуйте, MTD, Вы писали:


MTD>Хороший код сам является в значительной мере документацией. Можно обозвать метод open и сказать, что он открывает вообще все, детали смотрите в документации. А можно сделать классы File, TcpSocket, SerialPort с методами Open. Затем, метод Open класса File может две строки — путь к файлу и режим открытия, тут без документации не разобраться. А может принимать enum Mode { ReadOnly, WriteOnly, ... } и объект типа FilePath, который представляет собой путь к файлу.


Ага, а как ты узнаешь поведение в случае, если ты хочешь открыть файл для чтения, а он — ридонли? Из документации только, как ты не расписывай API.

L>>на каких платформах (если поддерживает больше одной)


MTD>Сова затрещала. Нормальная библиотека имеет список поддерживаемых платформ — вот ответ. Библиотека которая ведет себя по разному на разных платформах отправляется в топку.


Еще раз — разные платформы имеют право вести себя по-разному. Это обязано быть отражено в документации.

L>>и файловых системах и как они будут работать


MTD>Ага и на каких дисках, например WD-only, тебе же голову больше занять нечем. Файл — понятие высокоуровневое, забивать себе голову файловыми системами, секторами и блоками как-то неадекватно.


Сектора и блоки ты сам сюда притащил. Это называется "довести до абсурда".
А вот про системы, в которых файлов вообще может не быть, тебе уже сказали
www.blinnov.com
Re[11]: Комментарии
От: landerhigh Пират  
Дата: 20.09.14 16:21
Оценка:
Здравствуйте, RonWilson, Вы писали:

RW>расскажите практические примеры, у меня фантазия закончилась на угрозах семье, но код стал реально чище


Фи, как грубо.
Тоньше надо действовать, тоньше. Для разработчика что самое страшное? Что его код могут НЕ ДОПУСТИТЬ К КОММИТУ!!!
Вот отсюда и надо плясать
www.blinnov.com
Re[12]: Комментарии
От: niXman Ниоткуда https://github.com/niXman
Дата: 20.09.14 16:25
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Для разработчика что самое страшное? Что его код могут НЕ ДОПУСТИТЬ К КОММИТУ!!!

ой не факт.
знаю я одного, для которого самое страшное было в том, если его код для обсуждения опубликуют на каких-то форумах
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[10]: Комментарии
От: MTD https://github.com/mtrempoltsev
Дата: 20.09.14 16:40
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Ага, а как ты узнаешь поведение в случае, если ты хочешь открыть файл для чтения, а он — ридонли?


Сильно подозреваю, что он откроется нормально и его можно будет прочесть.

L>Из документации только, как ты не расписывай API.


Что значит расписывай API? При чем тут документация, если мы комментарии в коде обсуждали?

L>Еще раз — разные платформы имеют право вести себя по-разному. Это обязано быть отражено в документации.


Нет. Кроссплатформенная библиотека — абстракция над разными платформами, вести себя она должна одинаково иначе не понятно, зачем она нужна, проще нативные библиотеки использовать.

L>Сектора и блоки ты сам сюда притащил. Это называется "довести до абсурда".


Ты с этого начал, я просто сказал, что логично ожидать, что функция с названием OpenFile не будет форматировать диск.

L>А вот про системы, в которых файлов вообще может не быть, тебе уже сказали


И что? Давай развивай тему, поговорим о системах с памятью в 4 Кб, о системах без мониторов, с вотчдогами, с 9 битами в байте. Разное бывает, дальше то что?
Re[11]: Комментарии
От: landerhigh Пират  
Дата: 20.09.14 20:56
Оценка: :)
Здравствуйте, MTD, Вы писали:

L>>Ага, а как ты узнаешь поведение в случае, если ты хочешь открыть файл для чтения, а он — ридонли?


MTD>Сильно подозреваю, что он откроется нормально и его можно будет прочесть.


Сколько самолетов упало и сколько педалей газа заклинило из-за вот такого "сильно подозреваю"...

L>>Из документации только, как ты не расписывай API.


MTD>Что значит расписывай API? При чем тут документация, если мы комментарии в коде обсуждали?


Совершенно верно. Коменты в коде. Каждый юнит кода должен обосновывать свое существование, описывать, как он работает и почему он работает именно так, а не иначе и сообщать всю необходимую для его использования информацию.

L>>Еще раз — разные платформы имеют право вести себя по-разному. Это обязано быть отражено в документации.


MTD>Нет. Кроссплатформенная библиотека — абстракция над разными платформами, вести себя она должна одинаково иначе не понятно, зачем она нужна, проще нативные библиотеки использовать.


поведение файловой системы определяется платформой. Кроссплатформенная библиотека не всегда может или имеет право это поведение менять.

L>>Сектора и блоки ты сам сюда притащил. Это называется "довести до абсурда".


MTD>Ты с этого начал, я просто сказал, что логично ожидать, что функция с названием OpenFile не будет форматировать диск.


А вот что она будет делать при попытке открытия файла, которого нет? тут вариантов до чертиков и все они вполне логичные.
www.blinnov.com
Re[8]: Оцените качество кода на С++
От: aik Австралия  
Дата: 21.09.14 02:38
Оценка: +1
Здравствуйте, -n1l-, Вы писали:

N>В принципе я тоже так считаю, для макросов норм, но так не очень.


да она и в макросах не за этим, а чтоб компилятор был доволен.
Re[12]: Комментарии
От: MTD https://github.com/mtrempoltsev
Дата: 21.09.14 05:22
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>>>Ага, а как ты узнаешь поведение в случае, если ты хочешь открыть файл для чтения, а он — ридонли?


MTD>>Сильно подозреваю, что он откроется нормально и его можно будет прочесть.


L>Сколько самолетов упало и сколько педалей газа заклинило из-за вот такого "сильно подозреваю"...


Во-первых, при разработке самолетов требования особые, если так разрабатывать обычный софт то он будет стоить неприемлемо дорого.
Во-вторых, поделись, что должно по-твоему произойти при открытии рид-онли файла на чтение?
В-третьих, проблемы надежности комментарии вообще никак не решают.

L>Совершенно верно. Коменты в коде.


Документация — это не комменты в коде.

L>Каждый юнит кода должен обосновывать свое существование, описывать, как он работает и почему он работает именно так, а не иначе и сообщать всю необходимую для его использования информацию.


Если цель осваивать бюджеты при нулевом выхлопе без такого не обойтись.

L>поведение файловой системы определяется платформой. Кроссплатформенная библиотека не всегда может или имеет право это поведение менять.


Значит библиотека не смогла предоставить адекватную абстракцию, то не нужна такая библиотека, надо использовать нативные.

L>А вот что она будет делать при попытке открытия файла, которого нет? тут вариантов до чертиков и все они вполне логичные.


Форматировать диск?
Отредактировано 21.09.2014 5:41 MTD . Предыдущая версия .
Re[5]: Оцените качество кода на С++
От: aik Австралия  
Дата: 21.09.14 05:33
Оценка:
Здравствуйте, MTD, Вы писали:

Z>>например мы реализовали какой-то сложный математический

Z>>метод, нужен хотя бы комментарий, типа метод такой-то, см. статью такую-то,
MTD>Это единственный случай когда в хорошем коде комментарий уместен.

Единственный? Завидую, но не верю.

Почти любая оптимизация нуждается в комментариях почему она ничего не ломает (это часто "нелогичное" отсутствие лишних проверок, локов и тыдытыпы).
Любой обход (workaround) нуждается в комментариях что именно мы чиним (железо? косяк чужой библиотеки?).
Кусок какого нибудь протокола (сеть? ipc?) с подозрением на нелогичность требует комментариев.
Иногда бывает что кажется будто есть более простые/логичные варианты реализации функции, комментарий почему сделано так, а не иначе — нужен.

Ну, понятно, что комментировать в стиле void *p = malloc(256); /* Allocates memory */ нафиг не надо.
Re[6]: Оцените качество кода на С++
От: landerhigh Пират  
Дата: 21.09.14 07:31
Оценка: :)
Здравствуйте, aik, Вы писали:

aik>Ну, понятно, что комментировать в стиле void *p = malloc(256); /* Allocates memory */ нафиг не надо.


Даже здесь нужен комментарий, объясняющий почему void, и почему именно maloc и что это за магическая константа.
www.blinnov.com
Re[7]: Оцените качество кода на С++
От: aik Австралия  
Дата: 21.09.14 07:39
Оценка: 1 (1)
Здравствуйте, landerhigh, Вы писали:

aik>>Ну, понятно, что комментировать в стиле void *p = malloc(256); /* Allocates memory */ нафиг не надо.

L>Даже здесь нужен комментарий, объясняющий почему void, и почему именно maloc и что это за магическая константа.

Неа, вот тут как раз и не нужен, потому что код должен быть типа:

struct super_struct *xxx = malloc(sizeof(*xxx) * MAX_SUPER_OBJECTS_NUM);

и никаких комментариев. И почему именно malloc — надо писать если это c++ или glibc (а лучше заменять на new/g_malloc), а если голый си + libc — то зачем это комментировать?
Re[13]: Комментарии
От: landerhigh Пират  
Дата: 21.09.14 08:26
Оценка: 1 (1) :)
Здравствуйте, MTD, Вы писали:

L>>Сколько самолетов упало и сколько педалей газа заклинило из-за вот такого "сильно подозреваю"...


MTD>Во-первых, при разработке самолетов требования особые, если так разрабатывать обычный софт то он будет стоить неприемлемо дорого.


Сказки. При разработке самолетов обкладывание кода бумажками совершенно особое, но документацию разработчики читают самую обычную.

MTD>Во-вторых, поделись, что должно по-твоему произойти при открытии рид-онли файла на чтение?


Конечно, я хотел написать "открытие ридонли файла на чтение и запись". И вот что при этом произодйет, и должна объяснять документация к коду

MTD>В-третьих, проблемы надежности комментарии вообще никак не решают.


L>>Совершенно верно. Коменты в коде.


MTD>Документация — это не комменты в коде.


Это. Коменты в коде — это документация к коду. Если у вас иначе, то мне вас жаль.

L>>Каждый юнит кода должен обосновывать свое существование, описывать, как он работает и почему он работает именно так, а не иначе и сообщать всю необходимую для его использования информацию.


MTD>Если цель осваивать бюджеты при нулевом выхлопе без такого не обойтись.


То-то нулевой выхлоп при диких бюджетах наблюдается в основном при подходе "некогда документацию писать, копать надо"
www.blinnov.com
Re[14]: Комментарии
От: MTD https://github.com/mtrempoltsev
Дата: 21.09.14 09:15
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Сказки. При разработке самолетов обкладывание кода бумажками совершенно особое, но документацию разработчики читают самую обычную.


А ты для самолетов что писал? Вот мне довелось немного пописать критически важный код, там подходы к надежности совсем другие, например, софт пишут 3 независимые команды, затем в зависимости от входных данных 3 разные программы выдают результат, действие производится только если совпали все 3 результата, иначе чрезвычайная ситуация. Комментарии в коде безопасности никак не добавляют, гораздо лучше, например, ассертами описывать все контракты.

MTD>>Документация — это не комменты в коде.


L>Это. Коменты в коде — это документация к коду. Если у вас иначе, то мне вас жаль.


Покажи свой код, дабы не быть голословным. Доксигеновские описания — это никак не комментарии в коде, кроме того, для адекватной документации их мало.

L>>>Каждый юнит кода должен обосновывать свое существование, описывать, как он работает и почему он работает именно так, а не иначе и сообщать всю необходимую для его использования информацию.


MTD>>Если цель осваивать бюджеты при нулевом выхлопе без такого не обойтись.


L>То-то нулевой выхлоп при диких бюджетах наблюдается в основном при подходе "некогда документацию писать, копать надо"


Qbit86 меня обвинил в увлечении теории, когда я сказал, что комментарии в коде бардака не решат и надо думать как и что писать. Ты ударился в другую крайность — описывать вообще все. Извини, это ты менеджерам рассказывай, что все красиво оформлял, поэтому показать нечего, а я за взвешенный подход.

И не уходи от вопроса с которого началась дискуссия — адекватно функции открытия файла диск форматировать?
Re[6]: Комментарии
От: Evgeny.Panasyuk Россия  
Дата: 21.09.14 11:15
Оценка: +1
Говорить что код без комментариев плохой, также неверно как и говорить что комментарии не нужны.
Комментарии не всегда идут на пользу, точно также как не всегда оправданно и их отсутствие.

Неоднократно встречал как из комментариев делают карго-культ.
Например, возле пустого деструктора, который был абсолютно не нужен, стояло "это деструктор, он вызывается в конце жизни объекта" — и такого рода мусора по всему коду, перед каждой функцией, классом и строчкой кода, без всякой на то причины, аккуратненько так.
Такого рода комментарии как минимум раздражают, так как ожидаешь что-то осмысленное, несущее важную информацию — а там вода.

Комментарий это, очевидно, дополнительная сущность, которая отвлекает внимание, за которой нужно следить, держать в синхронизации с кодом.
Если комментарий не добавляет никакой новой информации способствующей пониманию кода, в то время как всё и так моментально ясно после беглого взгляда — то такой комментарий приносит вред.
Более того, нужно искать варианты замены комментария более простым и выразительным кодом. Тривиальный и классический пример — литерал с комментарием заменить константой с говорящим именем.

В то же время, при реализации каких-то нетривиальных концепций, с которыми читающий не обязан быть знаком, комментарии действительно необходимы.
В общем, как и всегда, нужно искать баланс и не допускать перекосы в ту или иную сторону
Re[15]: Комментарии
От: landerhigh Пират  
Дата: 21.09.14 11:58
Оценка: 12 (1)
Здравствуйте, MTD, Вы писали:


L>>Сказки. При разработке самолетов обкладывание кода бумажками совершенно особое, но документацию разработчики читают самую обычную.


MTD>А ты для самолетов что писал?


Прежде чем доставать линейку, предлагаю тебе подумать еще раз. А потом еще раз. А потом еще. Иначе конфуз получится.

MTD>Комментарии в коде безопасности никак не добавляют, гораздо лучше, например, ассертами описывать все контракты.



Имя того критически важного софта не затруднит привести? С ассертами.

MTD>>>Документация — это не комменты в коде.


L>>Это. Коменты в коде — это документация к коду. Если у вас иначе, то мне вас жаль.


MTD>Покажи свой код, дабы не быть голословным.


Коммерческая тайна.

MTD>Доксигеновские описания — это никак не комментарии в коде, кроме того, для адекватной документации их мало.


Значит, вы их готовить не умеете. Обычная ситуация, впрочем.

L>>>>Каждый юнит кода должен обосновывать свое существование, описывать, как он работает и почему он работает именно так, а не иначе и сообщать всю необходимую для его использования информацию.


MTD>>>Если цель осваивать бюджеты при нулевом выхлопе без такого не обойтись.


L>>То-то нулевой выхлоп при диких бюджетах наблюдается в основном при подходе "некогда документацию писать, копать надо"


MTD>Qbit86 меня обвинил в увлечении теории, когда я сказал, что комментарии в коде бардака не решат и надо думать как и что писать. Ты ударился в другую крайность — описывать вообще все. Извини, это ты менеджерам рассказывай, что все красиво оформлял, поэтому показать нечего, а я за взвешенный подход.


Это ты тут чего-то навыдумывал, приписал это собеседникам и теперь героически со своими же страхами борешься. Для начала потрудись доказать, что "красиво офомленный код" — это "нечего показать", что ли.

MTD>И не уходи от вопроса с которого началась дискуссия — адекватно функции открытия файла диск форматировать?


Не знаю, может быть для вашего свех-критического софта и адекватно
www.blinnov.com
Re[7]: Комментарии
От: landerhigh Пират  
Дата: 21.09.14 12:06
Оценка: :)
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Например, возле пустого деструктора, который был абсолютно не нужен, стояло "это деструктор, он вызывается в конце жизни объекта" — и такого рода мусора по всему коду, перед каждой функцией, классом и строчкой кода, без всякой на то причины, аккуратненько так.


Так бывает, когда требование о комментировании спускается сверху, а у разрабов нет даже зачатков инженерной культуры, чтобы понимать, зачем вообще нужны комментарии. И отсутствует лидер, который пряником, кнутом, огнем, мечом и инквизицией им эти зачатки привьет.
Я же сразу отметил бестолковость комментариев, присутствующих в коде, приведенном ТС и полное отсутствие комментариев там, где надо.

такая же история, кстати, случается и с юнит-тестами.

(хотя я сам комментирую объявление pure virtual destructor. На разный лад, а с определенной поры и на разных языках. Это моя фирменная фишка)

EP>Такого рода комментарии как минимум раздражают, так как ожидаешь что-то осмысленное, несущее важную информацию — а там вода.


EP>Комментарий это, очевидно, дополнительная сущность, которая отвлекает внимание, за которой нужно следить, держать в синхронизации с кодом.

EP>Если комментарий не добавляет никакой новой информации способствующей пониманию кода, в то время как всё и так моментально ясно после беглого взгляда — то такой комментарий приносит вред.

Комментарий должен помогать в использовании кода. Кратко сообщать о том, что это за класс, зачем он нужен и как и в каких случаях используется, какие задачи решает, а какие-нет (в случае, если это неочевидно). Очень хорошо написать, какие паттерны используются. Хороший комментарий в заголовке класса убирает необходимость заглядывать в определение методов от слова совсем.
www.blinnov.com
Re[16]: Комментарии
От: MTD https://github.com/mtrempoltsev
Дата: 21.09.14 13:09
Оценка:
Здравствуйте, landerhigh, Вы писали:

MTD>>А ты для самолетов что писал?


L>Прежде чем доставать линейку, предлагаю тебе подумать еще раз. А потом еще раз. А потом еще. Иначе конфуз получится.


Это ты все мериться пытаешься, а я просто поинтересовался есть ли у тебя такой опыт. Так что думай.

L>Имя того критически важного софта не затруднит привести? С ассертами.


Билибинская, Курская, Ростовская АЭС. Но ассерты не для тех кто свято верит, что комментарии спасут от всего. Я же предпочитаю, что программа аварийно завершиться в случае нарушения контракта, чем будет функционировать непредсказуемо.

MTD>>Покажи свой код, дабы не быть голословным.


L>Коммерческая тайна.


Быстро слился.

MTD>>Доксигеновские описания — это никак не комментарии в коде, кроме того, для адекватной документации их мало.


L>Для начала потрудись доказать, что "красиво офомленный код" — это "нечего показать", что ли.


Будь добр, читай что пишешь, ничего не понятно.

MTD>>И не уходи от вопроса с которого началась дискуссия — адекватно функции открытия файла диск форматировать?


L>Не знаю, может быть для вашего свех-критического софта и адекватно


Это серьезный ответ?
Re[8]: Комментарии
От: Evgeny.Panasyuk Россия  
Дата: 21.09.14 13:10
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Я же сразу отметил бестолковость комментариев, присутствующих в коде, приведенном ТС и полное отсутствие комментариев там, где надо.


Кстати да, я как раз о подобном и говорил — "CSewingDlg(CWnd* pParent = NULL); // стандартный конструктор".

L>такая же история, кстати, случается и с юнит-тестами.


Да, там тоже карго-культ цветёт и пахнет в виде каких-то формальных отписок.

L>(хотя я сам комментирую объявление pure virtual destructor. На разный лад, а с определенной поры и на разных языках. Это моя фирменная фишка)


Это, естественно, другого рода ситуация.

L>Комментарий должен помогать в использовании кода. Кратко сообщать о том, что это за класс, зачем он нужен и как и в каких случаях используется, какие задачи решает, а какие-нет (в случае, если это неочевидно).


Скажем так, мне трудно представить чем может быть вреден подобный комментарий для нетривиальных классов. Часто такой комментарий действительно оправдан, и даже необходим.

L>Очень хорошо написать, какие паттерны используются.


Смотря какого уровня класс, и что за паттерн. Например, комментировать что FooVisitor это "Visitor pattern" — избыточно, а вот сказать что обработка событий реализована как Proactor — уж точно ничем не повредит.

L>Хороший комментарий в заголовке класса убирает необходимость заглядывать в определение методов от слова совсем.


Кстати, немного offtopic — предпочитаю данные класса помещать в начало описания. Зачастую полезно знать не просто что делает класс, а представлять как именно он реализован, например чтобы оценить производительность/потребление ресурсов. Часто, краем глаза взглянув на структуру данных класса, сразу становится понятно что именно за птица и как она летает.
Re[8]: Комментарии
От: MTD https://github.com/mtrempoltsev
Дата: 21.09.14 13:13
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Комментарий должен помогать в использовании кода. Кратко сообщать о том, что это за класс, зачем он нужен и как и в каких случаях используется, какие задачи решает, а какие-нет (в случае, если это неочевидно).


Имя класса должно быть выбрано удачно, тогда будет очевидно зачем он нужен.
Re[17]: Комментарии
От: landerhigh Пират  
Дата: 21.09.14 13:16
Оценка:
Здравствуйте, MTD, Вы писали:

MTD>Это ты все мериться пытаешься, а я просто поинтересовался есть ли у тебя такой опыт. Так что думай.


L>>Имя того критически важного софта не затруднит привести? С ассертами.


MTD>Билибинская, Курская, Ростовская АЭС. Но ассерты не для тех кто свято верит, что комментарии спасут от всего. Я же предпочитаю, что программа аварийно завершиться в случае нарушения контракта, чем будет функционировать непредсказуемо.


Защитано, короче.
www.blinnov.com
Re[9]: Комментарии
От: landerhigh Пират  
Дата: 21.09.14 13:18
Оценка:
Здравствуйте, MTD, Вы писали:

MTD>Имя класса должно быть выбрано удачно, тогда будет очевидно зачем он нужен.


Ага, ага

ProactorVisitorObserverStateCacheManipulator


www.blinnov.com
Re[9]: Комментарии
От: landerhigh Пират  
Дата: 21.09.14 13:29
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

L>>Я же сразу отметил бестолковость комментариев, присутствующих в коде, приведенном ТС и полное отсутствие комментариев там, где надо.

EP>Кстати да, я как раз о подобном и говорил — "CSewingDlg(CWnd* pParent = NULL); // стандартный конструктор".

+

L>>такая же история, кстати, случается и с юнит-тестами.

EP>Да, там тоже карго-культ цветёт и пахнет в виде каких-то формальных отписок.

и метрик вроде "покрытия кода тестами".

L>>Комментарий должен помогать в использовании кода. Кратко сообщать о том, что это за класс, зачем он нужен и как и в каких случаях используется, какие задачи решает, а какие-нет (в случае, если это неочевидно).


EP>Скажем так, мне трудно представить чем может быть вреден подобный комментарий для нетривиальных классов. Часто такой комментарий действительно оправдан, и даже необходим.


Есть еще важные вещи, которые зачастую не замечают или не придают им значения. Например, берет ли класс владение неким объектом или нет, а если нет, то должно ли гарантироваться время жизни этого объекта и как. Еще пример — случается, что в конструктор передается некий умный указатель, но он используется исключительно в конструкторе и не сохраняется в данных класса. Подобные вещи лучше всего добавлять в комментарии, чтобы минимизировать количество сюрпризов при использовании.

L>>Очень хорошо написать, какие паттерны используются.


L>>Хороший комментарий в заголовке класса убирает необходимость заглядывать в определение методов от слова совсем.


EP>Кстати, немного offtopic — предпочитаю данные класса помещать в начало описания. Зачастую полезно знать не просто что делает класс, а представлять как именно он реализован, например чтобы оценить производительность/потребление ресурсов. Часто, краем глаза взглянув на структуру данных класса, сразу становится понятно что именно за птица и как она летает.


ну, это уже дело привычки, и иногда может идти в разрез с принятыми в компании code conventions.
www.blinnov.com
Re[10]: Комментарии
От: Evgeny.Panasyuk Россия  
Дата: 21.09.14 14:51
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Есть еще важные вещи, которые зачастую не замечают или не придают им значения. Например, берет ли класс владение неким объектом или нет, а если нет, то должно ли гарантироваться время жизни этого объекта и как.


Есть ряд типичных случаев, для которых есть общепринятые соглашения. В таких случаях комментарии не нужны.
А вот, например, если конструктор/метод берёт const Foo&, и сохраняет указатель в поле класса, то это нужно как-то обязательно обозначить.

L>Еще пример — случается, что в конструктор передается некий умный указатель, но он используется исключительно в конструкторе и не сохраняется в данных класса.


В таком случае нужно брать не умный указатель, а ссылку. Во-первых это бесплатно расширит область применимости, а во-вторых сделает комментарий необязательным (хотя и вреда он не принесёт).

L>Подобные вещи лучше всего добавлять в комментарии, чтобы минимизировать количество сюрпризов при использовании.


А вообще, да, нужно комментировать всякие неявные места, а иногда и явные, но не всем очевидные.
Re[18]: Комментарии
От: MTD https://github.com/mtrempoltsev
Дата: 21.09.14 15:46
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Защитано, короче.


Аргумент отличный, отдыхай
Re[10]: Комментарии
От: MTD https://github.com/mtrempoltsev
Дата: 21.09.14 15:49
Оценка:
Здравствуйте, landerhigh, Вы писали:

MTD>>Имя класса должно быть выбрано удачно, тогда будет очевидно зачем он нужен.


L>Ага, ага


L>
L>ProactorVisitorObserverStateCacheManipulator
L>


Специально для писателей еще раз: имя класса должно быть выбрано удачно — это раз, класс не должен быть перегружен функционалом — это два.
Re[11]: Комментарии
От: landerhigh Пират  
Дата: 21.09.14 15:56
Оценка:
Здравствуйте, MTD, Вы писали:

L>>
L>>ProactorVisitorObserverStateCacheManipulator
L>>


MTD>Специально для писателей еще раз: имя класса должно быть выбрано удачно — это раз, класс не должен быть перегружен функционалом — это два.


Ну вот надо классу одновременно быть и обсервером и посетителем. Выбирай имя. Только чтобы удачно.

Когда же уже сделают кнопочку "фейспалм"?
www.blinnov.com
Re[12]: Комментарии
От: MTD https://github.com/mtrempoltsev
Дата: 21.09.14 16:08
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Ну вот надо классу одновременно быть и обсервером и посетителем. Выбирай имя. Только чтобы удачно.


Зачем? Я инженер, опиши проблему, попробую помочь.

L>Когда же уже сделают кнопочку "фейспалм"?


Конструктивно.
Re[13]: Комментарии
От: landerhigh Пират  
Дата: 21.09.14 16:14
Оценка:
Здравствуйте, MTD, Вы писали:

L>>Ну вот надо классу одновременно быть и обсервером и посетителем. Выбирай имя. Только чтобы удачно.


MTD>Зачем? Я инженер, опиши проблему, попробую помочь.


Есть класс, который должен следить за обновлениями кеша. Для этого он подписывается на соответствующее событие, т.е. является обсервером.
Задача класса — когда скажут, обновить значения в некоей таблице, где он посетит каждую ячейку.

Придумывай название.

L>>Когда же уже сделают кнопочку "фейспалм"?


MTD>Конструктивно.


Весьма. Сэкономило бы кучу времени.
www.blinnov.com
Re[14]: Комментарии
От: MTD https://github.com/mtrempoltsev
Дата: 21.09.14 16:49
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Есть класс, который должен следить за обновлениями кеша. Для этого он подписывается на соответствующее событие, т.е. является обсервером.

L>Задача класса — когда скажут, обновить значения в некоей таблице, где он посетит каждую ячейку.

L>Придумывай название.


Зачем дополнительная сущность, зачем нарушение инкапсуляции? Почему бы не подписать таблицу на событие и она сама об этом не сообщит своим ячейкам в простом цикле?
Re[15]: Комментарии
От: landerhigh Пират  
Дата: 21.09.14 16:52
Оценка:
Здравствуйте, MTD, Вы писали:


L>>Придумывай название.


MTD>Зачем дополнительная сущность, зачем нарушение инкапсуляции? Почему бы не подписать таблицу на событие и она сама об этом не сообщит своим ячейкам в простом цикле?


Считай, что так нужно. Может, в целях оптимизации производительности, может, потому что сама таблица не может обработать это событие. т.к. требуются еще другие данные.
www.blinnov.com
Re[16]: Комментарии
От: MTD https://github.com/mtrempoltsev
Дата: 21.09.14 17:13
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Считай, что так нужно.


Увы, не аргумент.

L>Может, в целях оптимизации производительности


Не вижу в приведенном примере простора для оптимизации, если только этот класс не производит фильтрацию лишних событий об обновлении, тогда название становиться очевидным и да, в этом случаее ему все равно не нужно знание внутреннего устройства таблицы.

L>может, потому что сама таблица не может обработать это событие. т.к. требуются еще другие данные.


Значит надо передавать данные вместе с событием.
Re[17]: Комментарии
От: landerhigh Пират  
Дата: 21.09.14 17:36
Оценка:
Здравствуйте, MTD, Вы писали:


L>>Считай, что так нужно.


MTD>Увы, не аргумент.


Аргумент. Фантазия-то присутствет вообще? Например, класс таблицы неизменяем и не может сам подписаться. Живет в отдельном ActiveX. Или требуется недетская фильтрация, как ты уже догадался. Или требуется собирать данные из нескольких источников. Да мало ли что еще.

L>>Может, в целях оптимизации производительности


MTD>Не вижу в приведенном примере простора для оптимизации, если только этот класс не производит фильтрацию лишних событий об обновлении, тогда название становиться очевидным и да, в этом случаее ему все равно не нужно знание внутреннего устройства таблицы.


L>>может, потому что сама таблица не может обработать это событие. т.к. требуются еще другие данные.


MTD>Значит надо передавать данные вместе с событием.


Это не всегда возможно/оправдано.

Название-то придумал?
www.blinnov.com
Re[18]: Комментарии
От: MTD https://github.com/mtrempoltsev
Дата: 21.09.14 18:04
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Аргумент. Фантазия-то присутствет вообще?


Я не фантазер, оперирую строго фактами.

L>Например, класс таблицы неизменяем и не может сам подписаться. Живет в отдельном ActiveX.


Adapter

L>Или требуется недетская фильтрация, как ты уже догадался.


Filter

L>Или требуется собирать данные из нескольких источников.


Agregator

L>Да мало ли что еще.


Это разные классы с разной ответственностью. Немного отвлекись от документирования сферических коней и порефактори, в результате у тебя получится несколько классов с названиями отражающими то, чем они занимаются и ты сможешь из них выстроить цепочку, код станет понятней, более удобным в поддержке и менее подверженным багам. ProactorVisitorObserverStateCacheManipulator — это просто ужас, не надо так делать.

MTD>>Значит надо передавать данные вместе с событием.


L>Это не всегда возможно/оправдано.


Надо смотреть, но ProactorVisitorObserverStateCacheManipulator однозначно ни в какие ворота.

L>Название-то придумал?


Зачем? Мой вердикт — рефакторить.
Re[2]: (ключница) Си-шник писал?
От: Privalov  
Дата: 22.09.14 06:51
Оценка: 1 (1)
Здравствуйте, Basil2, Вы писали:

B>Обычно именно сишники выдают такой С++ код. Или драйверисты.


Это не сишник.
Судя по коду, его автор долго писал числодробилки на Фортране.
Думаю, что если бы обсуждаемый код был написан на Фортране 77 или даже Фортране 4, вопросов было бы гораздо меньше.
Re[7]: Оцените качество кода на С++
От: Хон Гиль Дон Россия  
Дата: 22.09.14 07:01
Оценка:
Здравствуйте, bnk, Вы писали:


ХГД>> Я, например, всегда комментирую "проваливающиеся" (без break) ветки в switch, или места, где важен порядок объявления членов класса.


bnk>Ага, классный принцип — закладывать мины и обставлять их флажками


Ну а какие варианты, если в классе вдруг нужны 3-5 зависимых друг от друга членов? Двухстадийная инициализация еще хуже, запихивать их в unique_ptr — ваще отстой. Так что это не мины, а вполне легитимные фичи языка.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[19]: Комментарии
От: landerhigh Пират  
Дата: 22.09.14 07:07
Оценка:
Здравствуйте, MTD, Вы писали:

MTD>Это разные классы с разной ответственностью. Немного отвлекись от документирования сферических коней и порефактори, в результате у тебя получится несколько классов с названиями отражающими то, чем они занимаются и ты сможешь из них выстроить цепочку, код станет понятней, более удобным в поддержке и менее подверженным багам. ProactorVisitorObserverStateCacheManipulator — это просто ужас, не надо так делать.


Теоретег. А что, если этот класс, котороый одновременно и визитор, и обсервер и еще кое-что, и есть результат рефакторинга? Просто потому, что так оказалось удобнее и логичнее?

L>>Название-то придумал?


MTD>Зачем? Мой вердикт — рефакторить.


То есть не смог придумать нормальное название — рефакторить?
www.blinnov.com
Re[7]: Оцените качество кода на С++
От: Хон Гиль Дон Россия  
Дата: 22.09.14 07:10
Оценка:
Здравствуйте, MTD, Вы писали:

ХГД>>Есть куча ситуаций, где недостаточно внимательный разработчик может нарваться на неприятности. Я, например, всегда комментирую "проваливающиеся" (без break) ветки в switch, или места, где важен порядок объявления членов класса.


MTD>Перепиши код проще, чтобы недостаточно внимательный разработчик не нарвался — гарантии, что он прочтет твой комментарий нет.


Эээ, ты правда считаешь, что код, полагающийся на гарантированный стандартом языка порядок инициализации членов класса, обязательно сложный?


ХГД>>Со сторонними библиотеками тоже всяких приколов хватает. Вообще, нарвался на неожиданность — прокомментируй.


MTD>Бывает, но мы же о хорошем коде фантазируем.


О хорошем, а не о нереально совершенном. Вот, например, библиотека boost::asio, по-твоему, достаточно хороша? Да и что нам asio, как на счет стандартных стримов? Заслуживают ли комментария их мелкие отличия на различных платформах?
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[8]: Оцените качество кода на С++
От: MTD https://github.com/mtrempoltsev
Дата: 22.09.14 16:17
Оценка:
Здравствуйте, Хон Гиль Дон, Вы писали:

MTD>>Перепиши код проще, чтобы недостаточно внимательный разработчик не нарвался — гарантии, что он прочтет твой комментарий нет.


ХГД>Эээ, ты правда считаешь, что код, полагающийся на гарантированный стандартом языка порядок инициализации членов класса, обязательно сложный?


Если инициализация зависит от порядка членов в классе — это плохо, сломать это может кто угодно, да даже ты сам, через некоторое время. Явное всегда лучше неявного, используй фабрику.

ХГД>О хорошем, а не о нереально совершенном. Вот, например, библиотека boost::asio, по-твоему, достаточно хороша?


Не знаю, не сталкивался.

ХГД>Да и что нам asio, как на счет стандартных стримов? Заслуживают ли комментария их мелкие отличия на различных платформах?


Как бы и сами разработчики не скрывают, что дизайн стримов неудачный.
Re[8]: Оцените качество кода на С++
От: MTD https://github.com/mtrempoltsev
Дата: 22.09.14 16:17
Оценка:
Здравствуйте, Хон Гиль Дон, Вы писали:

ХГД>Двухстадийная инициализация еще хуже, запихивать их в unique_ptr — ваще отстой.


Почему?
Re[20]: Комментарии
От: MTD https://github.com/mtrempoltsev
Дата: 22.09.14 16:23
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Теоретег.


Ребята, поймите — быть практиком, не значит писать говнокод. Быть практиком — это решать проблему приемлемые сроки минимизируя затраты с адекватным качеством. В этом очень помогают проверенные практики, часть из которых я затронул — проверка контрактов, осмысленное именование, один класс — одна ответственность, инкапсуляция.

L>А что, если


Слишком много если, для практика, ну видно же что пытаешься изобрести проблему.

L>этот класс, котороый одновременно и визитор, и обсервер и еще кое-что, и есть результат рефакторинга?


Рефактори дальше — результат неудовлетворительный.

MTD>>Зачем? Мой вердикт — рефакторить.


L>То есть не смог придумать нормальное название — рефакторить?


Как профессионал я не могу маскировать проблему, я ее решаю.
Re[3]: Оцените качество кода на С++
От: Andrew.W Worobow https://github.com/Worobow
Дата: 22.09.14 16:37
Оценка: 6 (1) +1
Здравствуйте, MTD, Вы писали:

AWW>>мало коментариев.


MTD>В хорошем коде комментариев быть не должно.


Вот это срачь то развели на пустом месте. Ога!

Коментарии это не только коментарии, это —
— советы наследнику например
— себе самому типа "что-то тут иногда не то пролетает"
— или заметки что надо поправить если будет медленно работать.

Да триллион ситуаций.
Коментарии которые коментируют очевидное, никому не нужны, а вот коментарии которые рассказывают почему выделяется память кусками не по размеру элемента а по какие-то магические 1024 байта. То есть отсуствие комментариев говорит, об отношении работника к своей работе. То есть попросу — ему насрать. ))
Не все кто уехал, предал Россию.
Re[21]: Комментарии
От: landerhigh Пират  
Дата: 22.09.14 16:38
Оценка:
Здравствуйте, MTD, Вы писали:

MTD>Здравствуйте, landerhigh, Вы писали:


L>>Теоретег.


MTD>Ребята, поймите — быть практиком, не значит писать говнокод. Быть практиком — это решать проблему приемлемые сроки минимизируя затраты с адекватным качеством. В этом очень помогают проверенные практики, часть из которых я затронул — проверка контрактов, осмысленное именование, один класс — одна ответственность, инкапсуляция.


Очень хорошие фразы.

L>>А что, если


MTD>Слишком много если, для практика, ну видно же что пытаешься изобрести проблему.


L>>этот класс, котороый одновременно и визитор, и обсервер и еще кое-что, и есть результат рефакторинга?


MTD>Рефактори дальше — результат неудовлетворительный.


Сказал кто? Ты видел код?

А вообще, я слегка покривил душой тут. Было время С++03, где не было ни лямбд, ни прочих вкусностей, а пользоваться std::bind_1st можно было только после пол-литры. И тогда, если твой класс хотел быть обсервером, самым простым способом было унаследовать интерфейс IObserver. Весело было, когда требовалось реализовывать два подобных интерфейса.
Вот тогда, бывало, смотришь на объявление класса и видишь, что он — кавалер ордена Визиторов дважды Обсервер Состояний. Даже если его имя этого не отражает.
Сейчас всю эту непубличную подноготную гораздо проще прятать под капот.
www.blinnov.com
Re[8]: Оцените качество кода на С++
От: landerhigh Пират  
Дата: 22.09.14 16:39
Оценка:
Здравствуйте, aik, Вы писали:

aik>struct super_struct *xxx = malloc(sizeof(*xxx) * MAX_SUPER_OBJECTS_NUM);


aik>и никаких комментариев. И почему именно malloc — надо писать если это c++ или glibc (а лучше заменять на new/g_malloc), а если голый си + libc — то зачем это комментировать?


Топик-то про C++
www.blinnov.com
Re[8]: Оцените качество кода на С++
От: Andrew.W Worobow https://github.com/Worobow
Дата: 22.09.14 16:41
Оценка: +1
Здравствуйте, landerhigh, Вы писали:

L>А подумать, что означает "потеря истории изменений", как она происходит и как и когда обнаруживается, и каким боком тут бекапы, подумать не пробовали, прежде чем написать?


Я представляю, какой срач поднимут некоторые увидев что-то такое :

#if 0
.... куча кода )))
#endif


Так обычно закрываются куски кода, которые выкинуты но пока еще нет 100% уверенности что это сделано правильно! ))
Не все кто уехал, предал Россию.
Re[21]: Комментарии
От: uzhas Ниоткуда  
Дата: 22.09.14 16:47
Оценка:
Здравствуйте, MTD, Вы писали:

MTD>Ребята, поймите — быть практиком, не значит писать говнокод.

быть практиком — это иногда писать говнокод

MTD> Быть практиком — это решать проблему приемлемые сроки минимизируя затраты с адекватным качеством. В этом очень помогают проверенные практики, часть из которых я затронул — проверка контрактов, осмысленное именование, один класс — одна ответственность, инкапсуляция.


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

L>>этот класс, котороый одновременно и визитор, и обсервер и еще кое-что, и есть результат рефакторинга?


MTD>Рефактори дальше — результат неудовлетворительный.

нет предела совершенству, надо уметь остановиться в рефакторинге

почему нельзя всегда давать хорошие имена — да потому что мы не писатели, мы программисты. если давать хорошие имена, то получится жава с длинными названями и эпические поэмы. ты, например, любишь поэмы, а я — хокку
программист борется с двумя сложностями :
1) сложность прикладной задачи
2) сложность решения прикладной задачи, т.к. попутно из-за "бест практик" ему приходится вводить сущности не из доменной области. а так, взятые с потолка

в общем, ты слишком категоричен и перфекционист Ж)
Re[9]: Оцените качество кода на С++
От: Хон Гиль Дон Россия  
Дата: 22.09.14 16:56
Оценка:
Здравствуйте, MTD, Вы писали:


ХГД>>Эээ, ты правда считаешь, что код, полагающийся на гарантированный стандартом языка порядок инициализации членов класса, обязательно сложный?


MTD>Если инициализация зависит от порядка членов в классе — это плохо, сломать это может кто угодно, да даже ты сам, через некоторое время.


Вот чтобы не сломали, я и пишу комментарии. Пока помогает.

MTD>Явное всегда лучше неявного, используй фабрику.


Фабрика-то тут при чем? Посмотри, например, rationale к boost::base_from_member — там описана похожая ситуация, только еще хуже, когда для обеспечения правильного порядка инициализации приходится выносить член в базовый класс.

ХГД>>О хорошем, а не о нереально совершенном. Вот, например, библиотека boost::asio, по-твоему, достаточно хороша?


MTD>Не знаю, не сталкивался.


Ну а с какой-нибудь хорошей кроссплатформенной плюсовой сетевой библиотекой сталкивался? Или таких нет в природе?


ХГД>>Да и что нам asio, как на счет стандартных стримов? Заслуживают ли комментария их мелкие отличия на различных платформах?


MTD>Как бы и сами разработчики не скрывают, что дизайн стримов неудачный.


Тем не менее, с ними приходится иметь дело. Значит и без комментариев не обойтись. Да не в дизайне там дело, а в особенностях реализиции.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[22]: Комментарии
От: MTD https://github.com/mtrempoltsev
Дата: 22.09.14 16:56
Оценка:
Здравствуйте, uzhas, Вы писали:

U>быть практиком — это иногда писать говнокод


Даже в таком случае практик локализует его и обернет красиво, сразу и не поймешь.

U>практики — это хорошо, только не только они есть. есть еще сроки, нервы и сложность решения и сопровождения


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

U>нет предела совершенству, надо уметь остановиться в рефакторинге


Золотые слова.

U>почему нельзя всегда давать хорошие имена — да потому что мы не писатели, мы программисты. если давать хорошие имена, то получится жава с длинными названями и эпические поэмы. ты, например, любишь поэмы, а я — хокку


Я тоже сторонник лаконичности, а в яве длинные имена от увлечения паттернами и прочей хренью, когда вместо терминов задачи вылезают термины решения — ProactorVisitorObserverStateCacheManipulator

U>в общем, ты слишком категоричен и перфекционист Ж)


Да это я пофлудить решил
Re[9]: Оцените качество кода на С++
От: Хон Гиль Дон Россия  
Дата: 22.09.14 16:57
Оценка:
Здравствуйте, MTD, Вы писали:

ХГД>>Двухстадийная инициализация еще хуже, запихивать их в unique_ptr — ваще отстой.


MTD>Почему?


А незачем без нужды хип теребить.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[10]: Оцените качество кода на С++
От: MTD https://github.com/mtrempoltsev
Дата: 22.09.14 17:00
Оценка:
Здравствуйте, Хон Гиль Дон, Вы писали:

ХГД>Ну а с какой-нибудь хорошей кроссплатформенной плюсовой сетевой библиотекой сталкивался? Или таких нет в природе?


Сокеты Беркли Писал сам над ними кросплатформенную обертку которая так и прижилась в компании.
Re[10]: Оцените качество кода на С++
От: MTD https://github.com/mtrempoltsev
Дата: 22.09.14 17:00
Оценка:
Здравствуйте, Хон Гиль Дон, Вы писали:

ХГД>>>Двухстадийная инициализация еще хуже, запихивать их в unique_ptr — ваще отстой.


MTD>>Почему?


ХГД>А незачем без нужды хип теребить.


Стек тоже не резиновый А двухстадийная инициализация чем не угодила?
Re[11]: Оцените качество кода на С++
От: Хон Гиль Дон Россия  
Дата: 22.09.14 17:06
Оценка: +1
Здравствуйте, MTD, Вы писали:

ХГД>>Ну а с какой-нибудь хорошей кроссплатформенной плюсовой сетевой библиотекой сталкивался? Или таких нет в природе?


MTD>Сокеты Беркли Писал сам над ними кросплатформенную обертку которая так и прижилась в компании.


Отличный подход — все писать самому с нуля. Вот только цену софта увеличивает в разы, зато комментариев минимум
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[11]: Оцените качество кода на С++
От: Хон Гиль Дон Россия  
Дата: 22.09.14 17:24
Оценка:
Здравствуйте, MTD, Вы писали:

ХГД>>>>Двухстадийная инициализация еще хуже, запихивать их в unique_ptr — ваще отстой.


MTD>>>Почему?


ХГД>>А незачем без нужды хип теребить.


MTD>Стек тоже не резиновый А двухстадийная инициализация чем не угодила?


А мне удобно, например, в одном классе держать дивайс (в терминологии boost::iostreams), фильтр (оттуда же), стримбуфер, стрим и архив (из boost::serialization). А архив не умеет двухстадийно инициализироваться. Да и стрим тоже хочет буфер прямо в конструктор. И вообще все эти open/close усложняют код так, что связываться с ними без действительно веской причины я не желаю.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[9]: Оцените качество кода на С++
От: aik Австралия  
Дата: 22.09.14 23:25
Оценка: :)
Здравствуйте, landerhigh, Вы писали:

L>Топик-то про C++


Неа, топик уже про то кто лучше кодер и нужны ли комментарии — один спорщик тянет в сторону что они не нужны вообще (детсад), другой — что нужны везде (зануда). Как то так
Re[10]: Оцените качество кода на С++
От: landerhigh Пират  
Дата: 23.09.14 06:36
Оценка: :)
Здравствуйте, aik, Вы писали:

aik>Здравствуйте, landerhigh, Вы писали:


L>>Топик-то про C++


aik>Неа, топик уже про то кто лучше кодер и нужны ли комментарии — один спорщик тянет в сторону что они не нужны вообще (детсад), другой — что нужны везде (зануда). Как то так


Если чо, то я за то, что комментировать надо интрефейсную часть юнитов, т.е. относиться к комментариям, как к документации API.
Потроха же лучше устраивать так, чтобы комментировать их было незачем.
www.blinnov.com
Re[8]: Оцените качество кода на С++
От: sraider http://dvinogradov.blogspot.com
Дата: 23.09.14 10:54
Оценка:
U>>эта "идиома" в плюсовом коде не нужна. в старом добром Си — на здоровье
K>Предложи наглядное решение на С++. Мне вот интересно.

Кстати, еще вариант:
[&]()
{
    xxx;
    if (!a) return;
    
    yyy;
    if (!b) return;
    
    zzz;
    if (!c) return;

    blablabla;
}();


А еще лучше — вместо лямбды сделать отдельную функцию. Читаться будет легче.
Re[11]: Оцените качество кода на С++
От: MTD https://github.com/mtrempoltsev
Дата: 23.09.14 19:15
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Если чо, то я за то, что комментировать надо интрефейсную часть юнитов, т.е. относиться к комментариям, как к документации API.


Поправка — не комментировать, а документировать, по-нормальному — с ссылками, описанием исключений и прочего, доксиген здесь кстати.

L>Потроха же лучше устраивать так, чтобы комментировать их было незачем.


У тебя что раздвоение личности? Когда я об этом говорил ты со мной спорил с пеной у рта
Re[12]: Оцените качество кода на С++
От: landerhigh Пират  
Дата: 23.09.14 19:29
Оценка: :)
Здравствуйте, MTD, Вы писали:

L>>Если чо, то я за то, что комментировать надо интрефейсную часть юнитов, т.е. относиться к комментариям, как к документации API.


MTD>Поправка — не комментировать, а документировать, по-нормальному — с ссылками, описанием исключений и прочего, доксиген здесь кстати.


Как хочешь называй. Ни того, ни другого в куске кода ТС и близко нет.

L>>Потроха же лучше устраивать так, чтобы комментировать их было незачем.


MTD>У тебя что раздвоение личности? Когда я об этом говорил ты со мной спорил с пеной у рта


Учу читать. Дорого:

Комментарий должен помогать в использовании кода. Кратко сообщать о том, что это за класс, зачем он нужен и как и в каких случаях используется, какие задачи решает, а какие-нет (в случае, если это неочевидно). Очень хорошо написать, какие паттерны используются. Хороший комментарий в заголовке класса убирает необходимость заглядывать в определение методов от слова совсем.


А теперь, краткий курс логики для планирующих сдавать IELTS!

Вышеприведенная цитата говорит о том, что:
1. Комментирование потрохов реализации обязательно.
2. Комментирование потрохов реализации излишне.
3. Not Given
www.blinnov.com
Re[13]: Оцените качество кода на С++
От: MTD https://github.com/mtrempoltsev
Дата: 23.09.14 20:07
Оценка: +1 -1
Здравствуйте, landerhigh, Вы писали:

L>

L>Кратко сообщать о том, что это за класс, зачем он нужен и как и в каких случаях используется, какие задачи решает, а какие-нет (в случае, если это неочевидно).


L>А теперь, краткий курс логики для планирующих сдавать IELTS!


L>Вышеприведенная цитата говорит о том, что:

L>1. Комментирование потрохов реализации обязательно.
L>2. Комментирование потрохов реализации излишне.
L>3. Not Given

Очевидно, пункт 1, так как далеко не все классы являются частью публичного интерфейса. А потом, ты вдруг выдаешь пункт 2:

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


Раздвоение личности?
Re[14]: Оцените качество кода на С++
От: landerhigh Пират  
Дата: 23.09.14 21:04
Оценка:
Здравствуйте, MTD, Вы писали:

L>>Вышеприведенная цитата говорит о том, что:

L>>1. Комментирование потрохов реализации обязательно.
L>>2. Комментирование потрохов реализации излишне.
L>>3. Not Given

MTD>Очевидно, пункт 1, так как далеко не все классы являются частью публичного интерфейса. А потом, ты вдруг выдаешь пункт 2:


MTD>

MTD>Потроха же лучше устраивать так, чтобы комментировать их было незачем.


MTD>Раздвоение личности?


Все гораздо проще — ты бы завалил IELTS и схожие тесты.
Правильный ответ — 3.
www.blinnov.com
Re[15]: Оцените качество кода на С++
От: MTD https://github.com/mtrempoltsev
Дата: 24.09.14 04:28
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Все гораздо проще — ты бы завалил IELTS и схожие тесты.

L>Правильный ответ — 3.

Уверен, сейчас ты приведешь доказательство где у меня ошибка, не балабол же ты в самом деле.
Re[16]: Оцените качество кода на С++
От: landerhigh Пират  
Дата: 24.09.14 06:36
Оценка:
Здравствуйте, MTD, Вы писали:

MTD>Здравствуйте, landerhigh, Вы писали:


L>>Все гораздо проще — ты бы завалил IELTS и схожие тесты.

L>>Правильный ответ — 3.

MTD>Уверен, сейчас ты приведешь доказательство где у меня ошибка, не балабол же ты в самом деле.


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

У тебе в элементарной внимательности ошибка.
Приведенная мной цитата ни слова не говорит о том, как нужно комментировать потроха и нужно ли вообще. Перечитай ее еще раз. А потом еще раз.

Комментарий должен помогать в использовании кода. Кратко сообщать о том, что это за класс, зачем он нужен и как и в каких случаях используется, какие задачи решает, а какие-нет (в случае, если это неочевидно). Очень хорошо написать, какие паттерны используются. Хороший комментарий в заголовке класса убирает необходимость заглядывать в определение методов от слова совсем.



Кстати, распространенная проблема среди населения вообще, не только погромиздов. Читают спеки и делают далеко идущие выводы на основе того, что им показалось, когда они это читали.
www.blinnov.com
Re[4]: Оцените качество кода на С++
От: Skorodum Россия  
Дата: 24.09.14 12:44
Оценка:
L>Заголовок с копирайтом позволяет сразу понять, что это, откуда оно взялось и кто в этом виноват. Даже когда работаешь из движущегося поезда без подключения к системе.

Git — everything is local.
Re[2]: Оцените качество кода на С++
От: B0FEE664  
Дата: 24.09.14 13:29
Оценка: +1
Здравствуйте, aik, Вы писали:

aik>лютый трындец. write-only.


Ничего подобного. Бывает много, много хуже. В коде, прошу заметить, всего одна глобальная переменная и числа с плавающей точкой сравниваются через == только в ассёртах.
И каждый день — без права на ошибку...
Re[5]: Оцените качество кода на С++
От: landerhigh Пират  
Дата: 24.09.14 14:35
Оценка:
Здравствуйте, Skorodum, Вы писали:

L>>Заголовок с копирайтом позволяет сразу понять, что это, откуда оно взялось и кто в этом виноват. Даже когда работаешь из движущегося поезда без подключения к системе.


S>Git — everything is local.


Сбросил тебе по шаре тимлид прототип, который он набросал за 15 минут на коленке в кафе из надерганных отовсюду файлов. Никакой гит тебе не поможет узнать, откуда что взялось и кто вообще это поддерживает.
www.blinnov.com
Re[17]: Оцените качество кода на С++
От: MTD https://github.com/mtrempoltsev
Дата: 24.09.14 17:03
Оценка:
Здравствуйте, landerhigh, Вы писали:

MTD>>Уверен, сейчас ты приведешь доказательство где у меня ошибка, не балабол же ты в самом деле.


L>Брать на слабо — это для детского сада.


Как можно! Попросили слова аргументировать — все обида.

L>Поумерь пыл, если хочешь, чтобы с тобой продолжали разговор.


А что ты в принципе можешь рассказать заслуживающего внимание?

L>У тебе в элементарной внимательности ошибка.

L>Приведенная мной цитата ни слова не говорит о том, как нужно комментировать потроха и нужно ли вообще. Перечитай ее еще раз. А потом еще раз.

Ну давай как для маленьких на пальцах.

Тезис: Комментарий должен помогать в использовании кода. Кратко сообщать о том, что это за класс, зачем он нужен и как и в каких случаях используется, какие задачи решает, а какие-нет (в случае, если это неочевидно).

Вывод: Нужно комментировать классы.

Факт: Не все классы являются частью публичного интерфейса.

Вывод: Нужно комментировать классы не являющиеся частью публичного интерфейса (потроха).

Вывод: Как минимум классы в потрохах комментировать нужно.

Где ошибка?

L>Кстати, распространенная проблема среди населения вообще, не только погромиздов. Читают спеки и делают далеко идущие выводы на основе того, что им показалось, когда они это читали.


Опять бла-бла-бла, становится скучно, что нибудь конкретное в тему есть что сказать?
Re[18]: Оцените качество кода на С++
От: landerhigh Пират  
Дата: 24.09.14 17:14
Оценка:
Здравствуйте, MTD, Вы писали:

L>>Поумерь пыл, если хочешь, чтобы с тобой продолжали разговор.


MTD>А что ты в принципе можешь рассказать заслуживающего внимание?


А ты?

L>>У тебе в элементарной внимательности ошибка.

L>>Приведенная мной цитата ни слова не говорит о том, как нужно комментировать потроха и нужно ли вообще. Перечитай ее еще раз. А потом еще раз.

MTD>Ну давай как для маленьких на пальцах.


MTD>Тезис: Комментарий должен помогать в использовании кода. Кратко сообщать о том, что это за класс, зачем он нужен и как и в каких случаях используется, какие задачи решает, а какие-нет (в случае, если это неочевидно).


MTD>Вывод: Нужно комментировать классы.


MTD>Факт: Не все классы являются частью публичного интерфейса.


Факт: переиспользуются не только классы, являющиеся частью публичного интерфейса. Публичного интерфейса как такового может и не быть. Потенциально вообще все юниты могут быть переиспользованы.
Факт: для эффективного переиспользования любых классов требуется наличие адекватной документации

MTD>Вывод: Нужно комментировать классы не являющиеся частью публичного интерфейса (потроха).


Налицо путаница в терминологии. Потроха — это детали реализации этих классов, которые как раз желательно писать так, чтобы туда нужды заглядывать вообще не было.

MTD>Вывод: Как минимум классы в потрохах комментировать нужно.


Комментировать нужно все юниты. Обычно это классы. Потроха этих классов, напротив, в комментировании нуждаются редко.

MTD>Где ошибка?


В терминологии.

L>>Кстати, распространенная проблема среди населения вообще, не только погромиздов. Читают спеки и делают далеко идущие выводы на основе того, что им показалось, когда они это читали.


MTD>Опять бла-бла-бла, становится скучно, что нибудь конкретное в тему есть что сказать?


Все четко и по теме.
www.blinnov.com
Re[19]: Оцените качество кода на С++
От: MTD https://github.com/mtrempoltsev
Дата: 24.09.14 17:39
Оценка:
Здравствуйте, landerhigh, Вы писали:

MTD>>А что ты в принципе можешь рассказать заслуживающего внимание?


L>А ты?


Тебе например, рассказал про проверки контрактов, инкапсуляцию, грамотное именование классов и разграничении ответственности, не моя беда, что ты не захотел слушать. А теперь ответь на вопрос.

L>>>У тебе в элементарной внимательности ошибка.


L>Факт: переиспользуются не только классы, являющиеся частью публичного интерфейса. Публичного интерфейса как такового может и не быть. Потенциально вообще все юниты могут быть переиспользованы.


Еще раз, где у меня ошибка? Снова в лужу сел и начал досочинять?

L>Факт: для эффективного переиспользования любых классов требуется наличие адекватной документации


Не обязательно, код сам по себе зачастую является документацией, которая к тому же всегда в актуальном состоянии.

L>Налицо путаница в терминологии. Потроха — это детали реализации этих классов, которые как раз желательно писать так, чтобы туда нужды заглядывать вообще не было.


Деталями реализации классов могут быть другие классы, о необходимости документирования которых ты твердишь, будь последователен.

MTD>>Где ошибка?


L>В терминологии.


Как уж на сковородке

L>Все четко и по теме.


Бла-бла-бла, тут ты мастер, видимо и по работе тебя знают как мастера объемной но бесполезной документации, а не производителя надежного кода
Re[20]: Оцените качество кода на С++
От: landerhigh Пират  
Дата: 24.09.14 17:43
Оценка:
Здравствуйте, MTD, Вы писали:

L>>Все четко и по теме.


MTD>Бла-бла-бла, тут ты мастер, видимо и по работе тебя знают как мастера объемной но бесполезной документации, а не производителя надежного кода


В очередной раз защитано
www.blinnov.com
Re[21]: Оцените качество кода на С++
От: MTD https://github.com/mtrempoltsev
Дата: 24.09.14 17:53
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>В очередной раз защитано


Надеюсь тебе полегчало, береги себя
Re[3]: Оцените качество кода на С++
От: ononim  
Дата: 22.01.15 18:05
Оценка: 3 (1) +2 :)
aik>>лютый трындец. write-only.
BFE>Ничего подобного. Бывает много, много хуже. В коде, прошу заметить, всего одна глобальная переменная и числа с плавающей точкой сравниваются через == только в ассёртах.
Угу, код конечно с первого взгляда выглядит пугающе, но если вчитаться — все это без труда можно отрефакторить. То что код не отрефакторил изначальный его писатель, негативно характеризует этого писателя, как черезчур упертого, а то что код не смогли отрефакторить все остальные члены команды — не лучшим образом характеризует уже их, как не очень умных любителей с нуля переписывать любой код, написанный без их участия. Как охарактеризовать менеджера этой команды, плачущегося в форуме и кидающегося кусками проприетарного кода и личными данными своих работников даже не знаю.. Но наверно они все созданы друг для друга...
Как много веселых ребят, и все делают велосипед...
Re[4]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 22.01.15 22:19
Оценка:
Мою ветку зачем то закрыли. Будто там не было принципиальных вопросов.
Это

Здравствуйте, ononim, Вы писали:

aik>>>лютый трындец. write-only.

BFE>>Ничего подобного. Бывает много, много хуже. В коде, прошу заметить, всего одна глобальная переменная и числа с плавающей точкой сравниваются через == только в ассёртах.
O>Угу, код конечно с первого взгляда выглядит пугающе, но если вчитаться — все это без труда можно отрефакторить. То что код не отрефакторил изначальный его писатель, негативно характеризует этого писателя, как черезчур упертого, а то что код не смогли отрефакторить все остальные члены команды — не лучшим образом характеризует уже их, как не очень умных любителей с нуля переписывать любой код, написанный без их участия. Как охарактеризовать менеджера этой команды, плачущегося в форуме и кидающегося кусками проприетарного кода и личными данными своих работников даже не знаю.. Но наверно они все созданы друг для друга...

Можно, но надо думать как. А я еще раз повторяю, алгоритм делался с нуля, отлаживался и дописывался, и времени на его приведение в "красивый вид" не было. Больше того, если бы заниматься красивостями попутно с написанием алгоритма, это было бы чревато и для алгоритма, и времени, но пользы бы не принесло особой. ПОСЛЕ написания и отладки, согласен, код надо рефакторить, если есть хорошие идеи. В противном случае получаем искусственно фрагментированный алгоритм, который вряд ли станет понятнее от множества "небольших функций".

На это есть возражения? А между тем болтовней о красивостях меня еще и отвлекали от разработки, отнимая полезное время.
Религия, догматизм в программировании далеко не лучший союзник. Когда под эту религию вынуждены прогибаться сотрудники, это не работа, а молельный дом.

Только дятел стал бы выкладывать сюда код. Но раз уж выложен, я спрашиваю, есть ли запрет на написание длинных функций? Не специально длинных, заметьте, а потому что алгоритм таков по природе. Я объяснял постановку задачи и споосб ее решения, так вот весь этот "длинный" код однороден и целостен, разбивать при желании его можно, но будет выглядеть неоправданным насилием.

Здесь уже согласились, что в работе с гитом возможны исключительные случаи, когда слияния бессмысленны. Почему же тут так упираются признать, что возможны и исключительные ситуации и с длиной кода, когда длинно не значит обязательно плохо?

Наконец, пара контрольных вопросов

1) Правильно ли, когда разработка алгоритма оценивается манагером вдвое дешевле ничего не значащего "сопровождения"?

2) Как вам ситуация, когда разработчик поставлен в условия самоокупаемости при разработке в основном разовых заказов от отечественных заказчиков?

Между тем такова была практика Крутого Манагера.
Отредактировано 22.01.2015 23:24 Юрий Лазарев . Предыдущая версия . Еще …
Отредактировано 22.01.2015 23:21 Юрий Лазарев . Предыдущая версия .
Отредактировано 22.01.2015 23:13 Юрий Лазарев . Предыдущая версия .
Re[5]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 22.01.15 23:29
Оценка:
Писали о юнит-тестах...

Если вы посмотрите на длинный код внимательно, вы увидите массу комментов, начинающихся у левого края. Это и были мои юнит-тесты, помеченные словом PARSE и пройденные и давно закрытые по причине завершения отладки каждого из предыдущих блоков текста.

Понимаю, до некоторых такое тяжело доходит. На то они и каста манагеров.
"Глупость это такой ум" (А.Лебедь)
Re[6]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 23.01.15 00:09
Оценка:
От прошлой ветки у меня сохранилось только это.
Отвечу.

Здравствуйте, dalmal, Вы писали:

D>Ох и портянок ты написал тут по треду, сколько надменности. Не будь таким, это же фу-фу-фу.

С волками жить. Не у вас ли тут в кодексе форума указано, что вы готовы презирать всех, кто ниже вас.

D>Если ты внимательно читал Макконнелла[]

D> отказ от притязаний на роль эксперта, если вы им не являетесь;

Ну а если являюсь?

D> охотное признание собственных ошибок;


Я всегда готов учиться на своих и чужих ошибках, и не считаю это "ужасным грехом", напротив. В отличие от (манагеров), этих упертых дятлов, которые их каждой ошибки делают проблему.

D>33.5. Общение и сотрудничество

D>По настоящему отличные программисты учатся эффективно сотрудничать, что всегда подразумевает написание удобочитаемого кода. Вероятно, компьютер читает вашу программу так же часто, как другие люди, но он читает плохой код гораздо лучше, чем люди. Работая над кодом, не забывайте про людей, которым придется изменять его в будущем. Программирование — это в первую очередь общение с другим программистом и только во вторую — с компьютером.
D>[/q]
D>Понимаешь? Общение важнее кода, так что нужно поумерить своё эго, не считать себя умнее других и уважать людей, с которыми ты работаешь. Независимо от их должности, а также от того, умнее ты их в самом деле или нет.
D>Тогда и вопросов у тебя таких не будет ко всем, а у начальства и коллектива к тебе.

Я объясню вам, как я это понимаю. Я принимаю всех людей равными себе, и потому неприемлю ни собственного уничижения, ни возвеличивания других без всякгого на то основания. Равенство есть залог нормальных отношений в коллективе, и когда я писал о настоящих своих коллегах, я писал о равных себе людях (хотя среди них были и профессора).

Уровень ума также не значит многого, если гарантировано равенство. Было бы интересно с человеком.
Но равенство предполагает ведь и равенство в оплате, верно?
Какое же уважение может заслуживать манагер, произвольно устанавливающий зряплату себе и остальным бездельникам?
Re: Оцените качество кода на С++
От: c-smile Канада http://terrainformatica.com
Дата: 23.01.15 00:10
Оценка:
Здравствуйте, GhostCoders, Вы писали:

"О чём поёшь, степняк?"
"О том что вижу о том и пою".

И дальше идет музыка тоскливая как свист ветра в коляючках саксаулов и аксакалов...

Код [похоже] делает две достаточно простые вещи собирает параметры диалога и трансформирует некий DOM из одной формы в другую на их основе.
Никакого заявленного Draw*** там и близко нет ...

Ну чего бы эти две функции по разным классам хотя бы не разнести ...
Фиг уже с этими model view controllers, не каждому эти слова даются, но здравый смысл-то должен же быть в концу концов...
Re[2]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 23.01.15 00:44
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Здравствуйте, GhostCoders, Вы писали:


Уважаемый c-smile, я — автор кода , GhostCoders никакого отношения к нему не имеет.
Все вопросы ко мне.
Напишите конкретнее, что за здравый смысл вам привиделся?
Re[4]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 23.01.15 00:47
Оценка: -1
Здравствуйте, ononim, Вы писали:

aik>>>лютый трындец. write-only.

BFE>>Ничего подобного. Бывает много, много хуже. В коде, прошу заметить, всего одна глобальная переменная и числа с плавающей точкой сравниваются через == только в ассёртах.
O>Угу, код конечно с первого взгляда выглядит пугающе, но если вчитаться — все это без труда можно отрефакторить.

Кстати, ловлю на слове. Давайте покажите, как этот код рефакторится, да еще без труда.
Докажите, что вы не трепло, в отличие от большинства здесь кидающихся бандерлогов.
Люблю учиться у умных людей.
Отредактировано 23.01.2015 0:49 Юрий Лазарев . Предыдущая версия .
Re[7]: Оцените качество кода на С++
От: Varavva  
Дата: 23.01.15 04:07
Оценка: +1
От ваших тем очень несет надменностью.
Вполне возможно, что этот ваш бывший начальник действительно не шибко умеет руководить. И то что он выложил сюда код, не сумев вам самостоятельно пояснить в чем проблема кода, характеризует его не с лучшей стороны.
Но вы гребете под одну гребенку всех кто выше вас по должности, говоря про неравенство в зарплате.
Видимо сказывается, что начальство уже давно моложе вам и это вас нереально бесит. Оттого и считаете всех за тупое говно.
Да, есть в IT такая проблема, что старики оказываются в джуниорах.
Но это не дает им право быть говном
Re: Оцените качество кода на С++
От: alexb1980  
Дата: 23.01.15 04:38
Оценка:
Здравствуйте, GhostCoders, Вы писали:

GC>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.


Я конечно особо не вникал в логику работы кода, но помоему тут целым namespace попахивает (вынести из этого dlg в классы логику) и таким образом "разгрузить" диалог.
... << RSDN@Home 1.2.0 alpha 5 rev. 1539>>
Re[2]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 23.01.15 05:21
Оценка:
Здравствуйте, alexb1980, Вы писали:

A>Здравствуйте, GhostCoders, Вы писали:


GC>>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.


A>Я конечно особо не вникал в логику работы кода, но помоему тут целым namespace попахивает (вынести из этого dlg в классы логику) и таким образом "разгрузить" диалог.


Да то, что диалог совмещен с алгоритмом, пусть вас не смущает. Это всегда можно разделить, но сейчас это несущественно. Разговор о самой длинной функции — как ее рефакторить? Говорят, без труда. Дайте просто идею, с чего начать. Без полной переработки — она не нужна, поезд уже ушел.

(Манагеру надо было выкладывать только эту одну функцию, а не весь почти проект. Если уж цепляться ко всему проекту в целом, то надо заметить, что отдельные моменты — это установки фирмы, а не мои изобретения. И я не хочу их защищать или поносить, мне они безразличны).
Отредактировано 23.01.2015 5:29 Юрий Лазарев . Предыдущая версия .
Re[8]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 23.01.15 05:27
Оценка:
Здравствуйте, Varavva, Вы писали:

V>От ваших тем очень несет надменностью.


Да, чувство равенства дает ощущение превосходства над теми бандерлогами, которые равенства не признают и всю жизнь живут по шакальи.

V>Видимо сказывается, что начальство уже давно моложе вам и это вас нереально бесит. Оттого и считаете всех за тупое говно.


Вот тут вы глубоко заблуждаетесь. Причина денег не может быть для меня определяющей. Это рассуждение для бандерлога, а не человека.

V>Да, есть в IT такая проблема, что старики оказываются в джуниорах.

V>Но это не дает им право быть говном

Сочувствую.
Re[9]: Оцените качество кода на С++
От: Varavva  
Дата: 23.01.15 05:51
Оценка:
Ладно. Простите, ради Бога.
Re[10]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 23.01.15 05:57
Оценка:
Здравствуйте, Varavva, Вы писали:

V>Ладно. Простите, ради Бога.


Да что вы, все путем.
Re[3]: Оцените качество кода на С++
От: ArtDenis Россия  
Дата: 23.01.15 07:20
Оценка:
Здравствуйте, Юрий Лазарев, Вы писали:

ЮЛ>Уважаемый c-smile, я — автор кода , GhostCoders никакого отношения к нему не имеет.

ЮЛ>Все вопросы ко мне.
ЮЛ>Напишите конкретнее, что за здравый смысл вам привиделся?

А темы про Крутого Манагера недостаточно?
http://rsdn.ru/forum/philosophy/5929295.1
Автор: Юрий Лазарев
Дата: 22.01.15
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[2]: Оцените качество кода на С++
От: MasterZiv СССР  
Дата: 23.01.15 12:37
Оценка:
Здравствуйте, niXman, Вы писали:

X>в общем, довольно не плохо с первого взгляда, видал и похуже %)

X>"порадовало" использование запятой:

Это плохо, однако!
Re[3]: Оцените качество кода на С++
От: niXman Ниоткуда https://github.com/niXman
Дата: 23.01.15 12:38
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>Это плохо, однако!

вам и правда необходим тег "сарказм"?
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Отредактировано 23.01.2015 12:39 niXman . Предыдущая версия .
Re[5]: Оцените качество кода на С++
От: ononim  
Дата: 23.01.15 12:50
Оценка: +1
aik>>>>лютый трындец. write-only.
BFE>>>Ничего подобного. Бывает много, много хуже. В коде, прошу заметить, всего одна глобальная переменная и числа с плавающей точкой сравниваются через == только в ассёртах.
O>>Угу, код конечно с первого взгляда выглядит пугающе, но если вчитаться — все это без труда можно отрефакторить.
ЮЛ>Кстати, ловлю на слове. Давайте покажите, как этот код рефакторится, да еще без труда.
ЮЛ>Докажите, что вы не трепло, в отличие от большинства здесь кидающихся бандерлогов.
Переводя на вашу лексику одну известную поговорку — если выйдя на улицу вы встретили бандерлога — значит вы встретили бандерлога. Если же вокруг вас все бандерлоги — значит вы бандерлог.
Весь код рефакторить не буду, мне еще есть чем заняться, но могу показать как я бы начал делать декомпозицию, даже не вникая пока в весь алгоритм. К примеру берем одну из самых длинных ф-ий — CSewingDlg::PseudoProcessAcceptedData. ВИдим цикл в самом начале. Видим коммент //PARSING TEST. Выносим весь этот цикл в ф-ю с название ParsingTest().
Видим следующий цикл, откоменченный как optimizing vertices (sorting). С ним сложнее — потому посмотрим что у него внутри. Натыкаемся на for (size_t j = 0; j < LineEnds.size(); j++). Смотрим что делает. А делает он подготовку концов отрезков, или чтото типа того. Потому пишем такую функцию
void PrepareLineEnds(std::vector<Edge>& LineEnds, std::vector<LineEnd> &EndsX)
{
        for (size_t j = 0; j < LineEnds.size(); j++)
        {
            //LineEnds[j].curve = 0;
            LineEnds[j].link_index = LineEnds[j].node_index1 = LineEnds[j].node_index2 = 0;
            LineEnds[j].back_count = LineEnds[j].back_index1 = LineEnds[j].back_index2 = 0;
            double x1 = LineEnds[j].x1;
            double y1 = LineEnds[j].y1;
            EndsX.push_back(LineEnd(x1, y1, (j + 1)));      // (ordered from 1!)
            double x2 = LineEnds[j].x2;
            double y2 = LineEnds[j].y2;
            EndsX.push_back(LineEnd(x2, y2, (j + 1)));      // (ordered from 1!)
        }

        std::sort(EndsX.begin(), EndsX.end(), LineEnd::SortedYpostX);

        for (size_t j = 0; j < EndsX.size(); j++)
        {
            size_t edge = EndsX[j].index;
            if (edge == 0) continue;
            if (LineEnds[edge - 1].back_count++ == 0)
                LineEnds[edge - 1].back_index1 = (j + 1);
            else LineEnds[edge - 1].back_index2 = (j + 1);
        }
}

...код под if (ii == 0 && ii0==1) ниче не делает — потому его тут нету. Это тоже не идеальная декомпозиция, но для первой итерации рефакторинга сойдет. Далее по тексту видим комент PARSING SORT — видимо последующий код можно выделить в ParsingSort(), но как я уже писал — я не подрядился перелопачивать весь ваш код, тут работы на несколько дней, а не на 10 минут. Вообще всю эту гиперфункцию PseudoProcessAcceptedData лучше вынести в отдельную сучность-класс, назвать ее AcceptrdDataPseudoProcessor а все что я написал выше, и что не написал — сделать ее методами. Про мелочи типа именования переменных и т.п. я уж не говорю (ii0 — это жесть). Ваш код для proof-of-concept вполне годный, но для продакшена его нужно переделать. К счастью его и _можно_ переделать, что бывает далеко не всегда.
Как много веселых ребят, и все делают велосипед...
Re[3]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 23.01.15 15:39
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>Здравствуйте, niXman, Вы писали:


X>>в общем, довольно не плохо с первого взгляда, видал и похуже %)

X>>"порадовало" использование запятой:

MZ>Это плохо, однако!


Так, а чем плохо использование запятой или вложенных тернарных операций, можете ясно ответить? Может, вам стоит написать Страустрапу, чтобы запретить запятую?
Re[6]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 23.01.15 16:10
Оценка:
Здравствуйте, ononim, Вы писали:

O>Весь код рефакторить не буду, мне еще есть чем заняться, но могу показать как я бы начал делать декомпозицию, даже не вникая пока в весь алгоритм. К примеру берем одну из самых длинных ф-ий — CSewingDlg::PseudoProcessAcceptedData. ВИдим цикл в самом начале. [] назвать ее AcceptrdDataPseudoProcessor а все что я написал выше, и что не написал — сделать ее методами. Про мелочи типа именования переменных и т.п. я уж не говорю (ii0 — это жесть). Ваш код для proof-of-concept вполне годный, но для продакшена его нужно переделать. К счастью его и _можно_ переделать, что бывает далеко не всегда.


Вообще, да, это первое что приходит в голову. Открою вам страшную тайну — примерно так я и сделал перед вторым этапом работы, именно, разбил код этой гиперфункции на 8 последовательных вызовов, с более-менее замкнутым содержанием в каждой. Но они все пользуются общими переменными, которые в класс тащить не очень хочется. Потому что это рабочие переменные, потому что их определение и время жизни уходит из-под контроля, они становятся для алгоритма в некоем смысле "глобальными", что конечно, не смертельно, но и не помогает в разработке. Поэтому я поступил проще — передаю их все как ссылки через параметры. Это временная мера, и также я продолжаю искать лучшее решение. Несмотря на то, что выглядят вызовы "ужасно", зато я сразу вижу все зависимости, где переменные возникают, где они используются.

Но в любом случае это насилие над кодом, реально не приносящее особых выгод. Слишком искусственный подход, еще немного, и получится обертка каждой строки. Я ищу семантический (осмысленный) способ рефакторинга. Скажем, если бы можно было придумать определенную алгебру узлов, я взял бы ее за основу класса безоговорочно.

Теперь с точки зрения разработки. Допустим, я пишу локальный цикл, еще не зная, насколько он будет полезен в окончательном алгоритме. Вы рекомендуете мне сразу оформлять его в виде метода класса, или подождать, пока код устоится? Если я его оформляю в метод, то 1) он отрывается визуально от контекста (а именно из контекста я решаю о его полезности), мне надо проделать лишнюю техническую работу по заведению метода, разрешить вопросы с переменными и т.д. В результате я выбрасываю этот метод.
Или же просто я пишу цикл по месту алгоритма, оцениваю его и при желании правлю или удаляю, никуда не перемещаясь. Результат получается тот же, но без лишних хлопот.

Поскольку логика алгоритма в основном последовательная, то нет большой беды в раздувании функции, — при исследовании каждого блока необходим лишь ближайший участок кода. Кажется, и у МакКонелла говорится об том же, что подобная последовательность кода не обязательно требует формального ограничения числа строк.

Если вернуться к аналогии с Войной и Миром, то заведение отдельных методов для мелких частей алгоритма подобно простановке названий параграфов в оглавлении длинной главы. Насколько это будет полезно? Кто читает главы по параграфам?
К слову сказать, те 8 подфункций, к которым я прибег, в этой аналогии приобретают смысл страниц — чисто технический прием для преодоления иэбыточного объема.
Отредактировано 23.01.2015 16:21 Юрий Лазарев . Предыдущая версия .
Re[7]: Оцените качество кода на С++
От: ononim  
Дата: 23.01.15 18:51
Оценка:
ЮЛ>Но в любом случае это насилие над кодом, реально не приносящее особых выгод. Слишком искусственный подход, еще немного, и получится обертка каждой строки. Я ищу семантический (осмысленный) способ рефакторинга. Скажем, если бы можно было придумать определенную алгебру узлов, я взял бы ее за основу класса безоговорочно.
Это не насилие над кодом, это дело тренировки восприятия + "дефолтовые настройки" этих особенностей у большинства людей. Большинство людей не могут держать в активной обработке большое количество сущностей без разделения их на иерархии. Да, можно ценой многолетней практики научится это делать, расширив количество одновременно обрабатываемых мозгом сущностей. И даже для такого человека работа с большим количеством однаранговых entities может быть эффективнее нежели работа над иерархией с "подгрузкой" в сознание каждого уровня по мере необходимости. Но надо отдавать себе отчет что код вы пишете не для себя, а для дяди, который вам платит за него денежку. Потому мнение этого дяди как минимум нужно принимать в расчет, иначе дядя вас может уволить, как видимо и произошло.
А дальше вопрос плавно перетекает в плоскость как вас можно было замотивировать сделать нужные изменения. Я думаю это тоже достаточно несложно можно было сделать, и то что этого не смог сделать менеджер — признак его непрофессионализма. Но методы мотивации вас лежат в не-технической плоскости, потому они скучны и вообще офтопны в этом разделе
Как много веселых ребят, и все делают велосипед...
Re[8]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 23.01.15 19:20
Оценка:
Здравствуйте, ononim, Вы писали:

ЮЛ>>Но в любом случае это насилие над кодом, реально не приносящее особых выгод. Слишком искусственный подход, еще немного, и получится обертка каждой строки. Я ищу семантический (осмысленный) способ рефакторинга. Скажем, если бы можно было придумать определенную алгебру узлов, я взял бы ее за основу класса безоговорочно.

O>Это не насилие над кодом, это дело тренировки восприятия + "дефолтовые настройки" этих особенностей у большинства людей. Большинство людей не могут держать в активной обработке большое количество сущностей без разделения их на иерархии. Да, можно ценой многолетней практики научится это делать, расширив количество одновременно обрабатываемых мозгом сущностей. И даже для такого человека работа с большим количеством однаранговых entities может быть эффективнее нежели работа над иерархией с "подгрузкой" в сознание каждого уровня по мере необходимости. Но надо отдавать себе отчет что код вы пишете не для себя, а для дяди, который вам платит за него денежку. Потому мнение этого дяди как минимум нужно принимать в расчет, иначе дядя вас может уволить, как видимо и произошло.
O>А дальше вопрос плавно перетекает в плоскость как вас можно было замотивировать сделать нужные изменения. Я думаю это тоже достаточно несложно можно было сделать, и то что этого не смог сделать менеджер — признак его непрофессионализма. Но методы мотивации вас лежат в не-технической плоскости, потому они скучны и вообще офтопны в этом разделе

Ну хорошо если +, а если -? Если "дефолтные настройки" не помогают в разработке? Вы такое исключаете?
Откуда берутся эти настройки? Не догматические ли это сущности?
Вы пытаетесь меня (себя) убедить в том, что бездумное заискивание благосклонности этого дяди (денюшки, какой аргумент!) важнее цели разработки?
Так что все же вы скажете, 200 строк на функцию — это табу, или признаете возможные исключения из правил (которые я вовсе не подвергаю сомнению с общей точки зрения разумности, вообще — но не в частности).
Re[9]: Оцените качество кода на С++
От: ononim  
Дата: 23.01.15 19:35
Оценка:
ЮЛ>>>Но в любом случае это насилие над кодом, реально не приносящее особых выгод. Слишком искусственный подход, еще немного, и получится обертка каждой строки. Я ищу семантический (осмысленный) способ рефакторинга. Скажем, если бы можно было придумать определенную алгебру узлов, я взял бы ее за основу класса безоговорочно.
O>>Это не насилие над кодом, это дело тренировки восприятия + "дефолтовые настройки" этих особенностей у большинства людей. Большинство людей не могут держать в активной обработке большое количество сущностей без разделения их на иерархии. Да, можно ценой многолетней практики научится это делать, расширив количество одновременно обрабатываемых мозгом сущностей. И даже для такого человека работа с большим количеством однаранговых entities может быть эффективнее нежели работа над иерархией с "подгрузкой" в сознание каждого уровня по мере необходимости. Но надо отдавать себе отчет что код вы пишете не для себя, а для дяди, который вам платит за него денежку. Потому мнение этого дяди как минимум нужно принимать в расчет, иначе дядя вас может уволить, как видимо и произошло.
O>>А дальше вопрос плавно перетекает в плоскость как вас можно было замотивировать сделать нужные изменения. Я думаю это тоже достаточно несложно можно было сделать, и то что этого не смог сделать менеджер — признак его непрофессионализма. Но методы мотивации вас лежат в не-технической плоскости, потому они скучны и вообще офтопны в этом разделе

ЮЛ>Ну хорошо если +, а если -? Если "дефолтные настройки" не помогают в разработке? Вы такое исключаете?

ЮЛ>Откуда берутся эти настройки? Не догматические ли это сущности?
Все люди разные. Дефолтовые настройки большинства мешают лично вашей разработке. Но код вы пишете не для себя, а для того самого большинства. Потому хотябы из соображений вежливости — перед передачей своего кода большинству — приведите его в тот вид, который оно сможет прожевать.

ЮЛ>Вы пытаетесь меня (себя) убедить в том, что бездумное заискивание благосклонности этого дяди (денюшки, какой аргумент!) важнее цели разработки?

Я не пытаюсь убеждать, я констатирую реальность Если ваша цель разработка — разрабатывайте как вам хочется, если ваша цель заработать денег дяде, чтобы он часть этих денег потом отдал вам — придется учитывать желания других. Кроме говоря про разработку — не забывайте что разработку эту ведете не вы один. Ну если вы ее собираетесь вести один — это ваше желание может не совпасть с желанием дяди. А дядя это не убеждения, дядя — это реальность. Иначе велком в шаровару — пишите софт сами, и сами его и продавайте.

ЮЛ>Так что все же вы скажете, 200 строк на функцию — это табу, или признаете возможные исключения из правил (которые я вовсе не подвергаю сомнению с общей точки зрения разумности, вообще — но не в частности).

У меня есть одно правило в жизни — не иметь правил в жизни Нужно смотреть каждую ситуацию в отдельности исходя из общих соображений оптимизации трудозатрат для разработки и поддержки продукта. В данном случае код можно и нужно побить на кусочки. Кстати можете если не лень провести длительный эксперимент — через пару месяцев (а лучше дольше) отсутствия работы с этим кодом отдайте его кому нить, и попросите внести какой нить мелкий баг. Ну или попробуйте модифицировать условия задачи. Потом приведите код в нужный вид.
Как много веселых ребят, и все делают велосипед...
Re[10]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 23.01.15 20:15
Оценка:
Здравствуйте, ononim, Вы писали:

O>Все люди разные. Дефолтовые настройки большинства мешают лично вашей разработке. Но код вы пишете не для себя, а для того самого большинства. Потому хотябы из соображений вежливости — перед передачей своего кода большинству — приведите его в тот вид, который оно сможет прожевать.

O>Я не пытаюсь убеждать, я констатирую реальность Если ваша цель разработка — разрабатывайте как вам хочется, если ваша цель заработать денег дяде, чтобы он часть этих денег потом отдал вам — придется учитывать желания других. Кроме говоря про разработку — не забывайте что разработку эту ведете не вы один.

Видимо, вы не поняли обстоятельств. Код этот находился еще в разработке. Разработка прерывалась из-за резких поворотов манагера — он то хотел обслуживать заказчиков, то не хотел. Меня он оставил на самоокупаемость. Вы пробовали такой способ работы, да еще с отечественным заказчиком? Разумеется, никакой команды не было. Да если бы и была, с этой командой я потерял бы только больше времени и возможно не смог выполнить даже сделанного.

O>Ну если вы ее собираетесь вести один — это ваше желание может не совпасть с желанием дяди. А дядя это не убеждения, дядя — это реальность. Иначе велком в шаровару — пишите софт сами, и сами его и продавайте.

Кто вам сказал, что я собираюсь / собирался работать один? Вы всегда пользуетесь домыслами?

O>У меня есть одно правило в жизни — не иметь правил в жизни Нужно смотреть каждую ситуацию в отдельности исходя из общих соображений оптимизации трудозатрат для разработки и поддержки продукта. В данном случае код можно и нужно побить на кусочки.

На любые кусочки, или все же есть стратегия? Или как дядя скажет?

O>Кстати можете если не лень провести длительный эксперимент — через пару месяцев (а лучше дольше) отсутствия работы с этим кодом отдайте его кому нить, и попросите внести какой нить мелкий баг. Ну или попробуйте модифицировать условия задачи. Потом приведите код в нужный вид.

Давайте лучше я внесу баг в ваш самый лучший код. И вы его моментально найдете, через 2 месяца. Пустые заклинания.
Кстати, я такой опыт фактически уже провел — тот исходный код, что вы невзлюбили, уже существует в стадии второго и успешного релиза, с новым алгоритмом внешнего контура, который я даже не надеялся написать.
Re[3]: Оцените качество кода на С++
От: alexb1980  
Дата: 23.01.15 20:55
Оценка:
Здравствуйте, Юрий Лазарев, Вы писали:

ЮЛ>Здравствуйте, alexb1980, Вы писали:


A>>Здравствуйте, GhostCoders, Вы писали:


GC>>>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.


A>>Я конечно особо не вникал в логику работы кода, но помоему тут целым namespace попахивает (вынести из этого dlg в классы логику) и таким образом "разгрузить" диалог.


ЮЛ>Да то, что диалог совмещен с алгоритмом, пусть вас не смущает. Это всегда можно разделить, но сейчас это несущественно. Разговор о самой длинной функции — как ее рефакторить? Говорят, без труда. Дайте просто идею, с чего начать. Без полной переработки — она не нужна, поезд уже ушел.


ЮЛ>(Манагеру надо было выкладывать только эту одну функцию, а не весь почти проект. Если уж цепляться ко всему проекту в целом, то надо заметить, что отдельные моменты — это установки фирмы, а не мои изобретения. И я не хочу их защищать или поносить, мне они безразличны).


Ну я вижу что самая длинная функция:
void CSewingDlg::PrepareContoursMakeEquidistants(...)
поробуйте разбить задачу на подзадачи ещё раз (разрабатывая рисование графических примитивов вы это уже сделали). Можно выделетить подзадачи в этом методе на основе private или protected методов.

Но кое-что ещё, я невооружённым глазом вижу что код не пройдёт коде ревью (если на проекте оно есть, но у вас я так понял манагер не доволен), просто видно что код messy т.е. очень много lines of code просто комменты // /**/
... << RSDN@Home 1.2.0 alpha 5 rev. 1539>>
Re[4]: Оцените качество кода на С++
От: alexb1980  
Дата: 23.01.15 21:01
Оценка:
Здравствуйте, alexb1980, Вы писали:
A>Ну я вижу что самая длинная функция:
A> void CSewingDlg::PrepareContoursMakeEquidistants(...)
A> поробуйте разбить задачу на подзадачи ещё раз (разрабатывая рисование графических примитивов вы это уже сделали). Можно выделетить подзадачи в этом методе на основе private или protected методов.

A> Но кое-что ещё, я невооружённым глазом вижу что код не пройдёт коде ревью (если на проекте оно есть, но у вас я так понял манагер не доволен), просто видно что код messy т.е. очень много lines of code просто комменты // /**/


Вернее самая длинная функция скорее.
int CSewingDlg::PseudoProcessAcceptedData(...)

О, даже когда фунция кочается не видно в MFC вроде можно разделять так:
////////////////////////////////////////////////////////////////////////
... << RSDN@Home 1.2.0 alpha 5 rev. 1539>>
Re[4]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 23.01.15 21:59
Оценка:
Здравствуйте, alexb1980, Вы писали:

A> поробуйте разбить задачу на подзадачи ещё раз (разрабатывая рисование графических примитивов вы это уже сделали). Можно выделетить подзадачи в этом методе на основе private или protected методов.


A> Но кое-что ещё, я невооружённым глазом вижу что код не пройдёт коде ревью (если на проекте оно есть, но у вас я так понял манагер не доволен), просто видно что код messy т.е. очень много lines of code просто комменты // /**/


Я уже разбил на 8 подзадач. Но это все равно слишком формально. Теперь представим, что мы принимаем этот формальный подход и дальше. Нам рекомендуют разбить код на методы длиной не больше 20 строк. Это где то 80 методов. И что, такая каша малообусловленных методов (а к ним и названия придумать будет сложно) будет лучше , чем цельный алгоритм?

Как тяжело признаться себе в своих предрассудках!

Комменты — я уже писал, что это не просто комменты, а код тестов, уже завершенных. Все в комментах сдвинуто влево намеренно, чтобы была видна временность назначения этих лесов. По вашему, я стал бы упираться убрать эти комменты, которые и написаны были специально так, чтобы их легко было почистить?
Re[5]: Оцените качество кода на С++
От: alexb1980  
Дата: 24.01.15 11:36
Оценка:
Здравствуйте, Юрий Лазарев, Вы писали:

ЮЛ>Здравствуйте, alexb1980, Вы писали:


A>> Но кое-что ещё, я невооружённым глазом вижу что код не пройдёт коде ревью (если на проекте оно есть, но у вас я так понял манагер не доволен), просто видно что код messy т.е. очень много lines of code просто комменты // /**/


ЮЛ>Я уже разбил на 8 подзадач. Но это все равно слишком формально. Теперь представим, что мы принимаем этот формальный подход и дальше. Нам рекомендуют разбить код на методы длиной не больше 20 строк. Это где то 80 методов. И что, такая каша малообусловленных методов (а к ним и названия придумать будет сложно) будет лучше , чем цельный алгоритм?


ЮЛ>Как тяжело признаться себе в своих предрассудках!


ЮЛ>Комменты — я уже писал, что это не просто комменты, а код тестов, уже завершенных. Все в комментах сдвинуто влево намеренно, чтобы была видна временность назначения этих лесов. По вашему, я стал бы упираться убрать эти комменты, которые и написаны были специально так, чтобы их легко было почистить?


Сразу тоже ж ничего не получается, есть ещё один подход, которой редко используется в MFC (чаще в cross platform наверное). Можно использовать множественное наследование, т.е : public CDialog, protected BaseClass1, protected BaseClass2 {

таким образом эти методы (которых малопонятных у вас получается 80) хотябы не будут идти вперешку ...
... << RSDN@Home 1.2.0 alpha 5 rev. 1539>>
Re[7]: Оцените качество кода на С++
От: BulatZiganshin  
Дата: 24.01.15 12:44
Оценка:
Здравствуйте, Юрий Лазарев, Вы писали:

ЮЛ>Но в любом случае это насилие над кодом, реально не приносящее особых выгод. Слишком искусственный подход, еще немного, и получится обертка каждой строки. Я ищу семантический (осмысленный) способ рефакторинга. Скажем, если бы можно было придумать определенную алгебру узлов, я взял бы ее за основу класса безоговорочно.


вот! именно поэтому рефакторинг у твоего менеджера и стоит вдвое дороже написания, что это не просто механическое разбиение, а продумывание "алгебры" задачи, в которой её реализация будет смореться просто и изящно. это целое искусство, позволяющее превратить код, который просто работает, в "совершенный", который дальше можно развивать и сопровождать, даже если тебя трактор переедет

а ты для начала ответь на один вопрос — хочешь ли ты, чтобы твои коллеги смогли сопровождать твой код после твоего ухода? да/нет

ЮЛ>Теперь с точки зрения разработки. Допустим, я пишу локальный цикл, еще не зная, насколько он будет полезен в окончательном алгоритме.


скажу сразу — твой код я считаю отличным для прототипа [1], т.е. той стадии, когда код в дальнейшем может быть либо выкинут, либо зарефакторен. противоречия с Злым Менеджером у вас возникли именно из-за рефакторинга, верно?

[1] плюс-минус всякие детали — например, константы надо делать сразу символическими, иначе потом их хрен найдёшь, а проблемные места в коде я предпочитаю помечать словами "to do: ..." в комментах, так что grep может их все найти

ЮЛ>Если вернуться к аналогии с Войной и Миром, то заведение отдельных методов для мелких частей алгоритма подобно простановке названий параграфов в оглавлении длинной главы. Насколько это будет полезно? Кто читает главы по параграфам?


аналог книги, которую ты читаешь — это exe-файл. что же касается технологий написания книги, то даже у переводчиков есть специальные средства для организации работы, а уж у писателей — и подавно

что касается Галуа, то напомню, что при жизни никто его идей не понял вообще и он помер в нищете в приюте. ты также хочешь?

я тебе могу подкинуть код на 2000 строк, который никто в мире кроме автора не понимает. но при этом он его поддерживает и развивает в течении 15 лет, и отдаёт под BSD лицензией. это код лучшего в мире алгоритма сжатия — lzma. и вот тут никто особых притензий автору не предъявляет, хочешь — разбирайся, не хочешь — отвали

если ты будешь разрабатывать свои решения, продавать их заказчику и обеспечивать сопровождение, то это будет твоё личное дело — как обеспечить их сопровождаемость (и проблемы твоего заказчика, если он переоценил твою надёжность). если же ты работаешь за зарплату, то это задача менеджера — обеспечить их сопровождаемость и независимость от первоначального разработчика. поэтому я могу только посочувствовать твоемук ЗМ, который потерял два месяца, плюс твою з/п, соцотчисления и прочие расходы совершенно зазря: если тебе самому выдать подобный кусок кода, написанный без структуры и комментов другим программистом, то ты его выкинешьб нафиг со словами "что за криворукий ламер написал!" и предложишь переписать всё заново
Люди, я люблю вас! Будьте бдительны!!!
Re[6]: Оцените качество кода на С++
От: enji  
Дата: 24.01.15 13:38
Оценка:
Здравствуйте, Юрий Лазарев, Вы писали:

ЮЛ>Если вы посмотрите на длинный код внимательно, вы увидите массу комментов, начинающихся у левого края. Это и были мои юнит-тесты, помеченные словом PARSE и пройденные и давно закрытые по причине завершения отладки каждого из предыдущих блоков текста.


Это очень странные тесты. Обычно тесты находятся рядом с кодом и живут вместе с ним. Тесты можно прогнать при изменении и убедиться, что ничего не сломалось. Тесты можно дописать при появлении бага, на который они не срабатывали

ЮЛ>Понимаю, до некоторых такое тяжело доходит. На то они и каста манагеров.

ЮЛ>"Глупость это такой ум" (А.Лебедь)

При чем тут манагеры и глупость?
Re[7]: Оцените качество кода на С++
От: enji  
Дата: 24.01.15 13:40
Оценка:
Здравствуйте, Юрий Лазарев, Вы писали:

ЮЛ>С волками жить. Не у вас ли тут в кодексе форума указано, что вы готовы презирать всех, кто ниже вас.


щито?

ЮЛ>Ну а если являюсь?


А если нет? Скромность украшает человека...

ЮЛ>Но равенство предполагает ведь и равенство в оплате, верно?

ЮЛ>Какое же уважение может заслуживать манагер, произвольно устанавливающий зряплату себе и остальным бездельникам?

Сколько проблем оказывается исчезает, когда народ не знает о зп друг друга
Re[4]: Оцените качество кода на С++
От: enji  
Дата: 24.01.15 13:45
Оценка:
Здравствуйте, Юрий Лазарев, Вы писали:

ЮЛ>Так, а чем плохо использование запятой или вложенных тернарных операций, можете ясно ответить? Может, вам стоит написать Страустрапу, чтобы запретить запятую?


а зачем тебе запятая? Ты экономишь на скобках?

ИМХО, обычно операции разделяются точкой с запятой. Запятая применяется лишь там, где она необходима — в выражениях, блоках for и т.п.

Тут просто принцип наименьшего удивления должен работать. Если можно сделать как обычно, то зачем запутывать читающего?
Re[8]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 24.01.15 14:03
Оценка: -1
Здравствуйте, BulatZiganshin, Вы писали:

BZ>вот! именно поэтому рефакторинг у твоего менеджера и стоит вдвое дороже написания, что это не просто механическое разбиение, а продумывание "алгебры" задачи, в которой её реализация будет смореться просто и изящно. это целое искусство, позволяющее превратить код, который просто работает, в "совершенный", который дальше можно развивать и сопровождать, даже если тебя трактор переедет


Вы переоцениваете способности моего "менеджера". Если бы он ценил "продумывание "алгебры" задачи", то прекрасно понимал бы, что рефакторинг лучше проводить по уже готовому и работающему коду, а не лезть со своими установками в каждый неподходящий момент, и не учить со своим молитвенником, как писать код. Причина придирок была банальна — он не мог выложить такой длинный код в SVN, поскольку всеми силами старается угодить забугорному боссу. И тут любые здравые аргументы бессильны.
Для него лучше вообще ничего не писать, чем сделать что-то работающее, но не укладывающееся в мессу о чечевичной похлебке.

BZ>а ты для начала ответь на один вопрос — хочешь ли ты, чтобы твои коллеги смогли сопровождать твой код после твоего ухода? да/нет


Во первых, никаких коллег у маня там нет и не было. Была пара хлюстов с раздутым ЧСВ и сидящих на одном "сопровождении", на которое босс из-за бугра отстегивал, и уж конечно ничего полезного с этими явными подхалимами манагера у меня быть не могло. Умные коллеги (если бы таковые были) сумели бы сопровождать мой код, но тогда они не были бы подхалимами и не напевали бы всякую благоглупость из молитвенника начальника. В коде они никогда не хотели и не разбирались (даже в стандартном), семантика их не занимала, своих алгоритмов они мне так и не представили, вся их "проверка" сводилась к формальным правилам. Что может быть интересного в общении с роботами?

BZ>скажу сразу — твой код я считаю отличным для прототипа [1], т.е. той стадии, когда код в дальнейшем может быть либо выкинут, либо зарефакторен. противоречия с Злым Менеджером у вас возникли именно из-за рефакторинга, верно?


Не только. Здесь это только явный повод указать мне на дверь. Но подноготная была — недостаток финансирования. Начиная с первого уже моего проекта, который удался (хлюст, напортачивший с гитом, тихо сопел в стену и что то там шипел), с которым манагер выезжал на Компасовскую конференцию, он тут же пошел на попятную. Проект плохо покупают, потребителей в России нет, а потому "скрипач не нужен". Это к вопросу о том, кто за что отвечает. За продажи отвечает манагер. Так почему бы ему было не урезать себе любимому зарплату за собственную недоработку?

BZ>[1] плюс-минус всякие детали — например, константы надо делать сразу символическими, иначе потом их хрен найдёшь, а проблемные места в коде я предпочитаю помечать словами "to do: ..." в комментах, так что grep может их все найти


grep вообще не применялся на фирме. Проблемные места я выделял для себя сам, и рассказал уже как. Если бы были какие то правила насчет этого, я не имел бы ничего против.


BZ>что касается Галуа, то напомню, что при жизни никто его идей не понял вообще и он помер в нищете в приюте. ты также хочешь?


Что значит, я так хочу? Так хочет руководство, хотите вы. А вы считаете, за идеи свои не стоит бороться?

BZ>если ты будешь разрабатывать свои решения, продавать их заказчику и обеспечивать сопровождение, то это будет твоё личное дело — как обеспечить их сопровождаемость (и проблемы твоего заказчика, если он переоценил твою надёжность). если же ты работаешь за зарплату, то это задача менеджера — обеспечить их сопровождаемость и независимость от первоначального разработчика. поэтому я могу только посочувствовать твоемук ЗМ, который потерял два месяца, плюс твою з/п, соцотчисления и прочие расходы совершенно зазря: если тебе самому выдать подобный кусок кода, написанный без структуры и комментов другим программистом, то ты его выкинешьб нафиг со словами "что за криворукий ламер написал!" и предложишь переписать всё заново


Да никто на фирме не стал бы писать этот проект вообще — мозги прокомпассированы инструкциями. Их обычный коронный ответ "Это невозможно", или "Это невыгодно", и эти ответы обожает манагер. Едва я только пришел на фирму, я удивился, почему окошко плагина сделано немодальным, очень некрасиво. Мне заявили на чистом глазу, "Это невозможно", потому что так сказал какой то их авторитет. Через два часа изучения их кода я сделал первую правку, которая показала, что это более чем возможно. Они даже не догадывались , что окнами в Windows можно управлять каким угодно образом.
Если вы полагаете, что именно на мне ЗМ понес самые большие убытки, то как быть с теми хлюстами, что ничего не делая, огребали вдвое больше?


Я не имею ничего против, если на фирме финансовые затруднения, и приходится урезать з/п. Но так если получать помалу, так всем помалу. Или одни выживают за счет других?
Отредактировано 24.01.2015 14:25 Юрий Лазарев . Предыдущая версия .
Re[8]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 24.01.15 14:18
Оценка:
Здравствуйте, enji, Вы писали:

E>А если нет? Скромность украшает человека...


Зато бандерлога украшает бесцеремонность.
Re[9]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 24.01.15 14:22
Оценка:
Удаления тут нет, что ли?
Отредактировано 24.01.2015 14:30 Юрий Лазарев . Предыдущая версия . Еще …
Отредактировано 24.01.2015 14:24 Юрий Лазарев . Предыдущая версия .
Re[6]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 24.01.15 14:37
Оценка:
Здравствуйте, alexb1980, Вы писали:

A>Сразу тоже ж ничего не получается, есть ещё один подход, которой редко используется в MFC (чаще в cross platform наверное). Можно использовать множественное наследование, т.е : public CDialog, protected BaseClass1, protected BaseClass2 {

A>таким образом эти методы (которых малопонятных у вас получается 80) хотябы не будут идти вперешку ...

Безусловно, надо (было) искать, но надо было и писать алгоритм. В сжатые сроки. Ну вот это ваше предложение, чем оно в принципе улучшило бы читаемость кода? Не говорю о трудоемкости написания этого множественного наследования, которое потом придется и переписывать в другой какой нибудь вид.
Чем оно было бы лучше цельного алгоритма, в котором все отношения под рукой, никакой фрагментации кода?
Я всегда за хорошую идею, но против фрагментации. В данном случае идея фрагментацию не перевешивает.
Re[7]: Оцените качество кода на С++
От: alexb1980  
Дата: 24.01.15 14:48
Оценка:
Здравствуйте, Юрий Лазарев, Вы писали:

ЮЛ>Здравствуйте, alexb1980, Вы писали:


ЮЛ>Безусловно, надо (было) искать, но надо было и писать алгоритм. В сжатые сроки. Ну вот это ваше предложение, чем оно в принципе улучшило бы читаемость кода? Не говорю о трудоемкости написания этого множественного наследования, которое потом придется и переписывать в другой какой нибудь вид.

ЮЛ>Чем оно было бы лучше цельного алгоритма, в котором все отношения под рукой, никакой фрагментации кода?
ЮЛ>Я всегда за хорошую идею, но против фрагментации. В данном случае идея фрагментацию не перевешивает.

Тут возможно вы не правы, вот например в C# есть например sealed classes. Вот и в C++ можно что-то такое изобрести (это точно можно сделать).

Ну тогда нужно оптимизировать алгоритм, если вас не устраеват фрагментация. Тут я пока ничего предложить немогу, потомучто это трудоёмко.
... << RSDN@Home 1.2.0 alpha 5 rev. 1539>>
Re[8]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 24.01.15 14:55
Оценка:
Здравствуйте, alexb1980, Вы писали:

A>Ну тогда нужно оптимизировать алгоритм, если вас не устраеват фрагментация. Тут я пока ничего предложить немогу, потомучто это трудоёмко.


Именно что трудоемко. Так можете вы прямо сказать, чем так неугодна программа с цельным алгоритмом и что исключения на длину кода все же возможны? Или превышения числа строк должны караться увольнением?
Re[9]: Оцените качество кода на С++
От: alexb1980  
Дата: 24.01.15 15:08
Оценка:
Здравствуйте, Юрий Лазарев, Вы писали:

ЮЛ>Здравствуйте, alexb1980, Вы писали:


A>>Ну тогда нужно оптимизировать алгоритм, если вас не устраеват фрагментация. Тут я пока ничего предложить немогу, потомучто это трудоёмко.


ЮЛ>Именно что трудоемко. Так можете вы прямо сказать, чем так неугодна программа с цельным алгоритмом и что исключения на длину кода все же возможны? Или превышения числа строк должны караться увольнением?


Я сам несколько раз попадал под "горячую" руку (довольны менеджеры — недовольны заказчики, не довольны менеджеры — довольны заказчики, все довольны — разработчик не доволен), скорее это просто предлог для очередного сокращения...
... << RSDN@Home 1.2.0 alpha 5 rev. 1539>>
Re[10]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 24.01.15 16:18
Оценка:
Здравствуйте, alexb1980, Вы писали:

ЮЛ>>Именно что трудоемко. Так можете вы прямо сказать, чем так неугодна программа с цельным алгоритмом и что исключения на длину кода все же возможны? Или превышения числа строк должны караться увольнением?


A>Я сам несколько раз попадал под "горячую" руку (довольны менеджеры — недовольны заказчики, не довольны менеджеры — довольны заказчики, все довольны — разработчик не доволен), скорее это просто предлог для очередного сокращения...


Скорее всего. Начальник с сокращениями не церемонится. При мне за год сократил двоих. Просто так — работу сделали, больше не нужны. На самом деле не нужны менеджеры — посредники, общение исполнителя с заказчиком напрямую много эффективнее.
Так как с принципиальным вопросом — возможна ли ситуация, когда природа алгоритма требует длинного кода? Или надо непременно убивать время на (далеко не всегда оправданный) поиск улучшения, свертки кода?
Re[11]: Оцените качество кода на С++
От: elmal  
Дата: 24.01.15 17:02
Оценка:
Здравствуйте, Юрий Лазарев, Вы писали:

ЮЛ>Так как с принципиальным вопросом — возможна ли ситуация, когда природа алгоритма требует длинного кода? Или надо непременно убивать время на (далеко не всегда оправданный) поиск улучшения, свертки кода?

Хороший разработчик благодаря тому, что сразу не допускает длинного кода — не убивает время, а наоборот его экономит. Лично я конечно в лоб говнокод напишу да, быстрее. Процентов так на 10. Но на отладку говнокода я потрачу в 10 раз больше времени, чем если я потружусь, и не буду допускать спагетти. Да, я в курсе, что очень многие называющие себя программистами настолько стали круты в написании говнокода, что в состоянии терять на отладку спагетти кода не в 10 раз больше времени, а в 2 раза больше времени. Вот только нахрена этот навык развивать? Когда если выработать привычку сразу писать максимально нормально, то прирост производительности работы будет гораздо больше. Да, я в курсе, что таким навыком мало кто обладает. Да, я в курсе, что в российских институтах нормально писать не учат. Да, я в курсе, что очень тяжело попасть на проект с нормальным кодом, где можно этому учиться. Я в курсе, что даже чтение Макконела не выработает эту привычку. И я в курсе что это очень тяжело, этому можно учиться всю жизнь, и все равно не достигнешь совершенства.

Но также я в курсе, что умение сразу писать код дает больший прирост производительности труда, чем любые знания деталей реализации множественного наследования, умение разворачивать списки на бумажке, умение компилировать код в уме и заучивание деталей реализации всяких I = I++ + ++I. А раз это дает повышение производительности труда — именно это и следует в первую очередь совершенствовать, причем совершенствовать постоянно. Да, умение писать код нормально никак не поможет пройти 95 процентов собеседований, а зачастую будет наоборот зачтено в минус. Вот только это поможет потом в реальной работе, в умении всегда без проблем выдерживать сроки, или хотя бы выдавать хоть что то приемлемое в сроки.
Re[12]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 24.01.15 17:53
Оценка:
Здравствуйте, elmal, Вы писали:

E>Хороший разработчик благодаря тому, что сразу не допускает длинного кода ...


Совершенно с вами согласен, так вот и я не пишу длинного кода. Почти никогда. Но это не избавляет от случаев, когда такое приходится писать. О том и был вопрос —

Так как с принципиальным вопросом — возможна ли ситуация, когда природа алгоритма требует длинного кода? Или надо непременно убивать время на (далеко не всегда оправданный) поиск улучшения, свертки кода?

Значит ли то, что ваш хороший разработчик "сразу не допускает длинного кода" то, что он его вообще не пишет? Никакого. И в чем тогда выражается экономия?
Отредактировано 24.01.2015 17:58 Юрий Лазарев . Предыдущая версия . Еще …
Отредактировано 24.01.2015 17:56 Юрий Лазарев . Предыдущая версия .
Re[13]: Оцените качество кода на С++
От: elmal  
Дата: 24.01.15 19:06
Оценка:
Здравствуйте, Юрий Лазарев, Вы писали:

ЮЛ>Так как с принципиальным вопросом — возможна ли ситуация, когда природа алгоритма требует длинного кода? Или надо непременно убивать время на (далеко не всегда оправданный) поиск улучшения, свертки кода?

Я не алгоритмист. Но я не знаю ситуаций, когда природа алгоритма требует длинного кода. Когда был студентом, я считал, что таких алгоритмов, которые требуют длинного кода — до хрена и больше. С опытом я уже не очень могу представить таких ситуаций.

ЮЛ>Значит ли то, что ваш хороший разработчик "сразу не допускает длинного кода" то, что он его вообще не пишет? Никакого. И в чем тогда выражается экономия?

Хороший разработчик не пишет длинный код. Он автоматом его разбивает в тот момент, когда он начинает казаться длинным. Просто на рефлексах это делает, не задумываясь и практически не тратя времени.
Re[14]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 24.01.15 19:15
Оценка:
Здравствуйте, elmal, Вы писали:

E>Я не алгоритмист. Но я не знаю ситуаций, когда природа алгоритма требует длинного кода. Когда был студентом, я считал, что таких алгоритмов, которые требуют длинного кода — до хрена и больше. С опытом я уже не очень могу представить таких ситуаций.


Ну вот вам такой алгоритм. Не понял, с каким таким опытом, если вы не алгоритмист?

E>Хороший разработчик не пишет длинный код. Он автоматом его разбивает в тот момент, когда он начинает казаться длинным. Просто на рефлексах это делает, не задумываясь и практически не тратя времени.


Надеюсь не автоматом Калашникова? Просто на рефлексах... — обезьяна какая то вырисовывается.

Я так понимаю, вы говорите о кодах уже заранее отрепетированных в голове. Здесь алгоритм писался с нуля и до последнего момента был не определен окончательно.
Отредактировано 24.01.2015 19:17 Юрий Лазарев . Предыдущая версия .
Re[15]: Оцените качество кода на С++
От: chaotic-good  
Дата: 26.01.15 10:30
Оценка: +2
ЮЛ>Надеюсь не автоматом Калашникова? Просто на рефлексах... — обезьяна какая то вырисовывается.

Хамство какое-то вырисовывается.

ЮЛ>Я так понимаю, вы говорите о кодах уже заранее отрепетированных в голове. Здесь алгоритм писался с нуля и до последнего момента был не определен окончательно.


Алгоритмист-Юрий, вы можете сказать, например, какая сложность у вашего алгоритма?

Нормальный подход в данном случае:
1. спецификация или статья, описывающая алгоритм, чтобы другие люди могли ее прочитать и покритиковать
2. (опционально) эскизный проект на питоне или чем-нибудь подобном (хотя иногда это невозможно и приходится использовать C++ для эскизного проекта из за специфики задачи), чтобы можно было отладить алгоритм (внести изменения в спеку, если нужно)
3. реализация алгоритма (не в классе CSewingDlg а отдельно), чтобы можно было потестить, например подготовить набор верифицируемых кейсов в текстовом файле и прогнать тест на нем
4. заюзать полученый код в реальном приложении

Так выглядит работа нормального алгоритмиста/ресерчера в IT компании, JFYI.
Получается быстрее (так как задачи решаются по отдельности, сначала дизайн алгоритма, потом кодинг) и качественней (покрытие тестами, естественным образом получается отдельная сущьность в коде, отвечающая за алгоритм).
Отредактировано 26.01.2015 10:35 chaotic-good . Предыдущая версия . Еще …
Отредактировано 26.01.2015 10:34 chaotic-good . Предыдущая версия .
Re: Оцените качество кода на С++
От: antonio_banderas Россия  
Дата: 28.01.15 18:26
Оценка:
Здравствуйте, GhostCoders, Вы писали:

GC>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.


Скажите плз, на какую зарплату был взят сотрудник? Москва?
Т.е. для 40 тыс. в месяц наверно неплох, для 80 уже плох.
Re[2]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 28.01.15 19:52
Оценка:
Здравствуйте, antonio_banderas, Вы писали:

_>Здравствуйте, GhostCoders, Вы писали:


GC>>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.


_>Скажите плз, на какую зарплату был взят сотрудник? Москва?

_>Т.е. для 40 тыс. в месяц наверно неплох, для 80 уже плох.

Я думаю, ТС вам не ответит. Это провинция, делите все на 2. Последние месяцы жаба душила манагера платить и 10 тыс., но выхода не было — других мест не найти.
Тут еще момент. Дело в том, что в середине лета я сломал ногу, вынужден был потерять месяц и, само собой, за свой счет. Полностью потратив на аренду все свои сбережения, по выходе на работу я вскоре был осчастливлен новостью о моем скором сокращении. При том что заказчик был извещен о задержке с разработкой и согласен был ждать, ждать не изволил манагер. Он несколько раз разрывал договор с заказчиком, несмотря на то, что первый этап я вопреки всему успешно завершил; он меня уволил (забавные подробности есть в др.ветке), и второй этап, также пройденный без замечаний, я выполнил с заказчиком уже после увольнения, в прямом с ним общении — сейчас как раз неделя тестов у заказчика.
Re[3]: Оцените качество кода на С++
От: enji  
Дата: 29.01.15 06:20
Оценка:
Здравствуйте, Юрий Лазарев, Вы писали:

ЮЛ>Я думаю, ТС вам не ответит. Это провинция, делите все на 2. Последние месяцы жаба душила манагера платить и 10 тыс., но выхода не было — других мест не найти.

Ты не преувеличиваешь? У нас кассир в супере получает рублей 12 минимум. Город 150 тыс примерно

ЮЛ>Тут еще момент. Дело в том, что в середине лета я сломал ногу, вынужден был потерять месяц и, само собой, за свой счет.

Почему не по больничному?
Re[4]: Оцените качество кода на С++
От: Evgeniy Skvortsov Россия  
Дата: 29.01.15 08:29
Оценка:
Здравствуйте, enji, Вы писали:

E>Ты не преувеличиваешь? У нас кассир в супере получает рублей 12 минимум. Город 150 тыс примерно


Скажем так, это не преувеличение, а откровенная брехня!

В нашем гипере кассир получает 25.

1С-ники в нашей деревне год назад получали 45. Я предметно интересовался.

Я сам на второй работе на полставки в гос. учреждении получаю 11 чистыми на руки (вернее на карту), появляюсь там раз в неделю.
Отредактировано 29.01.2015 8:32 Evgeniy Skvortsov . Предыдущая версия .
Re[3]: Оцените качество кода на С++
От: elmal  
Дата: 29.01.15 09:12
Оценка:
Здравствуйте, Юрий Лазарев, Вы писали:

ЮЛ>Я думаю, ТС вам не ответит. Это провинция, делите все на 2. Последние месяцы жаба душила манагера платить и 10 тыс., но выхода не было — других мест не найти.

Ууу, если провинция, то понятно. Полное отсутствие даже начальной культуры разработки сплошняком, учиться не у кого зачастую, только по книжкам. С увольнением могу поздравить, как раз хороший случай что то поменять в жизни. Рекомендую валить в Москву на юниорскую позицию если возраст позволяет, года через полтора код будешь писать приемлемый. Да, придется на дошираке посидеть, возможно снимать комнату и тому подобное. Но затем, как будет опыт, уже по провинциальным меркам будешь мегакрут, так что можешь попробовать вернуться (я правда вернувшихся не знаю, Москва это такое место, откуда не возвращаются, либо там остаются — либо в буржундию валят), либо уже на съем однушки хватать будет, и будут перспективы роста, если напрячься и оставить провинциальный уровень потребления, то можно и квартиру купить. В любом случае — через полтора года поймешь что был не прав относительно своего текущего кода, материться на текущий код будешь еще круче, чем здесь матерятся, научишься писать его быстрее, и в то же время понятнее.
Re[5]: Оцените качество кода на С++
От: omikron Россия  
Дата: 02.02.15 15:15
Оценка:
Мб, goto в коде запрещен корпоративным стандартом? Сложно объяснить такую предубежденность.
Но смотрится забавно. Пригодится троллить code reviewer'ов.
Re[3]: Оцените качество кода на С++
От: antonio_banderas Россия  
Дата: 03.02.15 11:18
Оценка:
Здравствуйте, Юрий Лазарев, Вы писали:

GC>>>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.


_>>Скажите плз, на какую зарплату был взят сотрудник? Москва?


ЮЛ><skipped> Последние месяцы жаба душила манагера платить и 10 тыс., но <skipped>


Несмотря на всю свою говёность, имхо, для цены 10 тыс. рублей в месяц — код неплох.
Re[4]: Оцените качество кода на С++
От: c-smile Канада http://terrainformatica.com
Дата: 03.02.15 17:59
Оценка: +1 -1
Здравствуйте, antonio_banderas, Вы писали:

_>Несмотря на всю свою говёность, имхо, для цены 10 тыс. рублей в месяц — код неплох.


Станет ли этот код в два раза лучше если заплатить в два раза больше?

А вообще есть такой мудрый принцип "мы не настолько богаты чтобы покупать дешевые вещи".
Re[2]: Оцените качество кода на С++
От: wety Россия  
Дата: 09.02.15 08:22
Оценка:
Здравствуйте, Кодт, Вы писали:

Не силён в плюсах.
Подскажи, пожалуйста, есть ли в приведенном коде паттерны?
На мой взгляд, вышеприведенный код замусорен комментариями и сбит в один файл. Мне думается, чтобы код был бы читабельнее, если код можно было бы разнести по разным файликам, обрамить его в регионы, вынести в какие-нибудь header (или как там у вас в С++)?
Также я не увидел ни одного try...catch... finally, что очень и очень ужасно.
Re[3]: Оцените качество кода на С++
От: Кодт Россия  
Дата: 09.02.15 09:59
Оценка: 15 (2) +2
Здравствуйте, wety, Вы писали:

W>Здравствуйте, Кодт, Вы писали:


W>Не силён в плюсах.

W>Подскажи, пожалуйста, есть ли в приведенном коде паттерны?

Там и паттерны, и маттерны. Маттернов больше

W>На мой взгляд, вышеприведенный код замусорен комментариями и сбит в один файл. Мне думается, чтобы код был бы читабельнее, если код можно было бы разнести по разным файликам, обрамить его в регионы, вынести в какие-нибудь header (или как там у вас в С++)?


Хотя бы разбить на много функций.

W>Также я не увидел ни одного try...catch... finally, что очень и очень ужасно.


Это, как раз, не ужасно.
Во-первых, RAII избавляет от большинства finally. Это же С++.
Во-вторых, штатное исключение там — это исчерпание памяти. Но эта ситуация сама по себе настолько кровава, что нет существенной разницы между просто завершением программы и её вылетом с треском по terminate.
А все остальные исключительные ситуации — нештатные (например, отсутствие контрола с нужным идентификатором, или нулевой указатель из ком-объекта), и должны, максимум, покрываться ассертами и/или тестами.
Только если не стоит задача сделать программу устойчивой ко внутренним сбоям.
Перекуём баги на фичи!
Re: Оцените качество кода на С++
От: tapatoon  
Дата: 14.02.15 11:40
Оценка: +1
Здравствуйте, GhostCoders, Вы писали:

GC>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.

GC>...

Доказывать бесполезно, введите code review. 2 месяца и свой код от чужого перестанете отличать. Испытано на себе.
Центр ИПсО Сил Специальных Операций
Re[2]: Оцените качество кода на С++
От: Lepsik Индия figvam.ca
Дата: 18.02.15 21:11
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, GhostCoders, Вы писали:


GC>>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.


в описаниях функции не должно быть тела если это только не одна строка.
— иначе не оттестируешь

Ну и судя по стилю юнит тесты вообше не писались.

блоки ифоф без скобок — за такое бить надо железной линейкой по руках — как товаришь собирается ставить точку остановки при дебаге? Значить приличные системы не поддерживал.

В обшем говнокод, пардон мой френч.

Это только беглый стилистичецкий взгляд.
Re: Оцените качество кода на С++
От: andy1618 Россия  
Дата: 19.02.15 09:05
Оценка: +1
Здравствуйте, GhostCoders, Вы писали:

GC>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.



Посмотрел небольшой кусочек.
Если оценивать по внешнему виду — да, не фонтан (смешение гуи и логики,
длинные методы, местами магические числа, ...).
Но если смотреть на суть (сложная вычислительная геометрия), то код весьма и весьма неплох
и производит впечатление реально работающего. Бывает намного-намного хуже, и дело даже
не в "лапше" и однобуквенных переменных, а в потенциальных багах, вызванных непониманием
особенностей вещественных чисел, логических ошибках, не учтением краевых случаев, делений на 0 и т.п.


В целом, возникло ощущение, что крутого математика-алгоритмиста зачем-то посадили писать продакшн-код.
На мой взгляд, его дело — придумывать и описывать (может быть, в виде прототипа) алгоритмы,
которые 99% "стильно пишущих" программистов даже не поймут, не говоря уже о том, чтобы придумать
это самим (к RSDN эта статистика не относится ).
Re[2]: Оцените качество кода на С++
От: Юрий Лазарев Россия  
Дата: 19.02.15 20:24
Оценка: :)
Это и не был продакшн-код, в смысле, моему заказчику нужен был работающий бинарник, а не код. Другое дело, — начальству нужен был "красивый" код, чтобы показать боссу. Я не против писать "совершенный" код, только это делается вовсе не быстро, и не всегда способствует быстроте разработки. Ну и потворствовать лентяям, которым надо "с первого взгляда" понять, где баг, тоже нет резона. Во всяком случае, этот код в сравнении с , например, boost::Graph при поиске бага будет ничем не хуже (а Graph ничем не лучше).

Причина, стало быть, чисто формальна.
Re: Оцените качество кода на С++
От: Shmj Ниоткуда  
Дата: 11.02.16 17:09
Оценка:
Здравствуйте, GhostCoders, Вы писали:

GC>Данный код был написан моим сотрудником. Мне этот код не нравится, но он мне отвечает что я субъективен и его код неплох.


Сколько по времени заняло написание этого кода? Очень важный вопрос. Был ли написан на скорую руку за 1 месяц -- то неплохо. Если же месяца 2-3 ваял -- то мог бы и отрефакторить.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.